def test_split_operator_error_operator_VT_order_against_definition(self): hamiltonian = (normal_ordered(fermi_hubbard(3, 3, 1., 4.0)) - 2.3 * FermionOperator.identity()) potential_terms, kinetic_terms = ( diagonal_coulomb_potential_and_kinetic_terms_as_arrays( hamiltonian)) potential = sum(potential_terms, FermionOperator.zero()) kinetic = sum(kinetic_terms, FermionOperator.zero()) error_operator = ( split_operator_trotter_error_operator_diagonal_two_body( hamiltonian, order='V+T')) # V-then-T ordered double commutators: [V, [T, V]] + [T, [T, V]] / 2 inner_commutator = normal_ordered(commutator(kinetic, potential)) error_operator_definition = normal_ordered( commutator(potential, inner_commutator)) error_operator_definition += normal_ordered( commutator(kinetic, inner_commutator)) / 2.0 error_operator_definition /= 12.0 self.assertEqual(error_operator, error_operator_definition)
def test_erpa_eom_ham_lih(): filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5") molecule = MolecularData(filename=filename) reduced_ham = make_reduced_hamiltonian( molecule.get_molecular_hamiltonian(), molecule.n_electrons) rha_fermion = of.get_fermion_operator(reduced_ham) permuted_hijkl = np.einsum('ijlk', reduced_ham.two_body_tensor) opdm = np.diag([1] * molecule.n_electrons + [0] * (molecule.n_qubits - molecule.n_electrons)) tpdm = 2 * of.wedge(opdm, opdm, (1, 1), (1, 1)) rdms = of.InteractionRDM(opdm, tpdm) dim = 3 # so we don't do the full basis. This would make the test long full_basis = {} # erpa basis. A, B basis in RPA language cnt = 0 # start from 1 to make test shorter for p, q in product(range(1, dim), repeat=2): if p < q: full_basis[(p, q)] = cnt full_basis[(q, p)] = cnt + dim * (dim - 1) // 2 cnt += 1 for rkey in full_basis.keys(): p, q = rkey for ckey in full_basis.keys(): r, s = ckey for sigma, tau in product([0, 1], repeat=2): test = erpa_eom_hamiltonian(permuted_hijkl, tpdm, 2 * q + sigma, 2 * p + sigma, 2 * r + tau, 2 * s + tau).real qp_op = of.FermionOperator( ((2 * q + sigma, 1), (2 * p + sigma, 0))) rs_op = of.FermionOperator( ((2 * r + tau, 1), (2 * s + tau, 0))) erpa_op = of.normal_ordered( of.commutator(qp_op, of.commutator(rha_fermion, rs_op))) true = rdms.expectation(of.get_interaction_operator(erpa_op)) assert np.isclose(true, test)
def benchmark_commutator_diagonal_coulomb_operators_2D_spinless_jellium( side_length): """Test speed of computing commutators using specialized functions. Args: side_length: The side length of the 2D jellium grid. There are side_length ** 2 qubits, and O(side_length ** 4) terms in the Hamiltonian. Returns: runtime_commutator: The time it takes to compute a commutator, after partitioning the terms and normal ordering, using the regular commutator function. runtime_diagonal_commutator: The time it takes to compute the same commutator using methods restricted to diagonal Coulomb operators. """ hamiltonian = normal_ordered( jellium_model(Grid(2, side_length, 1.), plane_wave=False)) part_a = FermionOperator.zero() part_b = FermionOperator.zero() add_to_a_or_b = 0 # add to a if 0; add to b if 1 for term, coeff in hamiltonian.terms.items(): # Partition terms in the Hamiltonian into part_a or part_b if add_to_a_or_b: part_a += FermionOperator(term, coeff) else: part_b += FermionOperator(term, coeff) add_to_a_or_b ^= 1 start = time.time() _ = normal_ordered(commutator(part_a, part_b)) end = time.time() runtime_commutator = end - start start = time.time() _ = commutator_ordered_diagonal_coulomb_with_two_body_operator( part_a, part_b) end = time.time() runtime_diagonal_commutator = end - start return runtime_commutator, runtime_diagonal_commutator
def test_generalized_doubles(): generator = generate_antisymm_generator(2) nso = generator.shape[0] for p, q, r, s in product(range(nso), repeat=4): if p < q and s < r: assert np.isclose(generator[p, q, r, s], -generator[q, p, r, s]) ul, vl, one_body_residual, ul_ops, vl_ops, one_body_op = \ doubles_factorization_svd(generator) generator_mat = np.reshape(np.transpose(generator, [0, 3, 1, 2]), (nso**2, nso**2)).astype(np.float) one_body_residual_test = -np.einsum('pqrq->pr', generator) assert np.allclose(generator_mat, generator_mat.T) assert np.allclose(one_body_residual, one_body_residual_test) tgenerator_mat = np.zeros_like(generator_mat) for row_gem, col_gem in product(range(nso**2), repeat=2): p, s = row_gem // nso, row_gem % nso q, r = col_gem // nso, col_gem % nso tgenerator_mat[row_gem, col_gem] = generator[p, q, r, s] assert np.allclose(tgenerator_mat, generator_mat) u, sigma, vh = np.linalg.svd(generator_mat) fop = copy.deepcopy(one_body_op) fop2 = copy.deepcopy(one_body_op) fop3 = copy.deepcopy(one_body_op) fop4 = copy.deepcopy(one_body_op) for ll in range(len(sigma)): ul.append(np.sqrt(sigma[ll]) * u[:, ll].reshape((nso, nso))) ul_ops.append( get_fermion_op(np.sqrt(sigma[ll]) * u[:, ll].reshape((nso, nso)))) vl.append(np.sqrt(sigma[ll]) * vh[ll, :].reshape((nso, nso))) vl_ops.append( get_fermion_op(np.sqrt(sigma[ll]) * vh[ll, :].reshape((nso, nso)))) Smat = ul[ll] + vl[ll] Dmat = ul[ll] - vl[ll] S = ul_ops[ll] + vl_ops[ll] Sd = of.hermitian_conjugated(S) D = ul_ops[ll] - vl_ops[ll] Dd = of.hermitian_conjugated(D) op1 = S + 1j * of.hermitian_conjugated(S) op2 = S - 1j * of.hermitian_conjugated(S) op3 = D + 1j * of.hermitian_conjugated(D) op4 = D - 1j * of.hermitian_conjugated(D) assert np.isclose( of.normal_ordered(of.commutator( op1, of.hermitian_conjugated(op1))).induced_norm(), 0) assert np.isclose( of.normal_ordered(of.commutator( op2, of.hermitian_conjugated(op2))).induced_norm(), 0) assert np.isclose( of.normal_ordered(of.commutator( op3, of.hermitian_conjugated(op3))).induced_norm(), 0) assert np.isclose( of.normal_ordered(of.commutator( op4, of.hermitian_conjugated(op4))).induced_norm(), 0) fop3 += (1 / 8) * ((S**2 - Sd**2) - (D**2 - Dd**2)) fop4 += (1 / 16) * ((op1**2 + op2**2) - (op3**2 + op4**2)) op1mat = Smat + 1j * Smat.T op2mat = Smat - 1j * Smat.T op3mat = Dmat + 1j * Dmat.T op4mat = Dmat - 1j * Dmat.T assert np.allclose(of.commutator(op1mat, op1mat.conj().T), 0) assert np.allclose(of.commutator(op2mat, op2mat.conj().T), 0) assert np.allclose(of.commutator(op3mat, op3mat.conj().T), 0) assert np.allclose(of.commutator(op4mat, op4mat.conj().T), 0) # check that we have normal operators and that the outer product # of their eigenvalues is imaginary. Also check vv is unitary if not np.isclose(sigma[ll], 0): assert np.isclose( of.normal_ordered(get_fermion_op(op1mat) - op1).induced_norm(), 0) assert np.isclose( of.normal_ordered(get_fermion_op(op2mat) - op2).induced_norm(), 0) assert np.isclose( of.normal_ordered(get_fermion_op(op3mat) - op3).induced_norm(), 0) assert np.isclose( of.normal_ordered(get_fermion_op(op4mat) - op4).induced_norm(), 0) ww, vv = np.linalg.eig(op1mat) eye = np.eye(nso) assert np.allclose(np.outer(ww, ww).real, 0) assert np.allclose(vv.conj().T @ vv, eye) ww, vv = np.linalg.eig(op2mat) assert np.allclose(np.outer(ww, ww).real, 0) assert np.allclose(vv.conj().T @ vv, eye) ww, vv = np.linalg.eig(op3mat) assert np.allclose(np.outer(ww, ww).real, 0) assert np.allclose(vv.conj().T @ vv, eye) ww, vv = np.linalg.eig(op4mat) assert np.allclose(np.outer(ww, ww).real, 0) assert np.allclose(vv.conj().T @ vv, eye) fop2 += 0.25 * ul_ops[ll] * vl_ops[ll] fop2 += 0.25 * vl_ops[ll] * ul_ops[ll] fop2 += -0.25 * of.hermitian_conjugated( vl_ops[ll]) * of.hermitian_conjugated(ul_ops[ll]) fop2 += -0.25 * of.hermitian_conjugated( ul_ops[ll]) * of.hermitian_conjugated(vl_ops[ll]) fop += vl_ops[ll] * ul_ops[ll] true_fop = get_fermion_op(generator) assert np.isclose(of.normal_ordered(fop - true_fop).induced_norm(), 0) assert np.isclose(of.normal_ordered(fop2 - true_fop).induced_norm(), 0) assert np.isclose(of.normal_ordered(fop3 - true_fop).induced_norm(), 0) assert np.isclose(of.normal_ordered(fop4 - true_fop).induced_norm(), 0)
def doubles_factorization_svd(generator_tensor: np.ndarray, eig_cutoff=None): """ Given an antisymmetric antihermitian tensor perform a double factorized low-rank decomposition. Given: A = sum_{pqrs}A^{pq}_{sr}p^ q^ r s with A^{pq}_{sr} = -A^{qp}_{sr} = -A^{pq}_{rs} = -A^{sr}_{pq} Rewrite A as a sum-of squares s.t A = sum_{l}Y_{l}^2 where Y_{l} are normal operator one-body operators such that the spectral theorem holds and we can use the double factorization to implement an approximate evolution. """ if not np.allclose(generator_tensor.imag, 0): raise TypeError("generator_tensor must be a real matrix") if eig_cutoff is not None: if eig_cutoff % 2 != 0: raise ValueError("eig_cutoff must be an even number") nso = generator_tensor.shape[0] generator_tensor = generator_tensor.real generator_mat = np.zeros((nso**2, nso**2)) for row_gem, col_gem in product(range(nso**2), repeat=2): p, s = row_gem // nso, row_gem % nso q, r = col_gem // nso, col_gem % nso generator_mat[row_gem, col_gem] = generator_tensor[p, q, r, s] test_generator_mat = np.reshape( np.transpose(generator_tensor, [0, 3, 1, 2]), (nso**2, nso**2)).astype(np.float) assert np.allclose(test_generator_mat, generator_mat) if not np.allclose(generator_mat, generator_mat.T): raise ValueError("generator tensor does not correspond to four-fold" " antisymmetry") one_body_residual = -np.einsum('pqrq->pr', generator_tensor) u, sigma, vh = np.linalg.svd(generator_mat) ul = [] ul_ops = [] vl = [] vl_ops = [] if eig_cutoff is None: max_sigma = len(sigma) else: max_sigma = eig_cutoff for ll in range(max_sigma): ul.append(np.sqrt(sigma[ll]) * u[:, ll].reshape((nso, nso))) ul_ops.append( get_fermion_op(np.sqrt(sigma[ll]) * u[:, ll].reshape((nso, nso)))) vl.append(np.sqrt(sigma[ll]) * vh[ll, :].reshape((nso, nso))) vl_ops.append( get_fermion_op(np.sqrt(sigma[ll]) * vh[ll, :].reshape((nso, nso)))) S = ul_ops[ll] + vl_ops[ll] D = ul_ops[ll] - vl_ops[ll] op1 = S + 1j * of.hermitian_conjugated(S) op2 = S - 1j * of.hermitian_conjugated(S) op3 = D + 1j * of.hermitian_conjugated(D) op4 = D - 1j * of.hermitian_conjugated(D) assert np.isclose( of.normal_ordered(of.commutator( op1, of.hermitian_conjugated(op1))).induced_norm(), 0) assert np.isclose( of.normal_ordered(of.commutator( op2, of.hermitian_conjugated(op2))).induced_norm(), 0) assert np.isclose( of.normal_ordered(of.commutator( op3, of.hermitian_conjugated(op3))).induced_norm(), 0) assert np.isclose( of.normal_ordered(of.commutator( op4, of.hermitian_conjugated(op4))).induced_norm(), 0) one_body_op = of.FermionOperator() for p, q in product(range(nso), repeat=2): tfop = ((p, 1), (q, 0)) one_body_op += of.FermionOperator(tfop, coefficient=one_body_residual[p, q]) return ul, vl, one_body_residual, ul_ops, vl_ops, one_body_op