def test_fci_energy(): filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.hdf5") molecule = MolecularData(filename=filename) reduced_ham = make_reduced_hamiltonian( molecule.get_molecular_hamiltonian(), molecule.n_electrons) np_ham = of.get_number_preserving_sparse_operator( of.get_fermion_operator(reduced_ham), molecule.n_qubits, num_electrons=molecule.n_electrons, spin_preserving=True) w, _ = np.linalg.eigh(np_ham.toarray()) assert np.isclose(molecule.fci_energy, w[0]) 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) np_ham = of.get_number_preserving_sparse_operator( of.get_fermion_operator(reduced_ham), molecule.n_qubits, num_electrons=molecule.n_electrons, spin_preserving=True) w, _ = np.linalg.eigh(np_ham.toarray()) assert np.isclose(molecule.fci_energy, w[0])
def __init__(self, name, geometry, multiplicity, charge, n_orbitals, n_electrons, basis='sto-3g', frozen_els=None): self.name = name self.multiplicity = multiplicity self.charge = charge self.basis = basis self.geometry = geometry self.molecule_data = MolecularData(geometry=self.geometry, basis=basis, multiplicity=self.multiplicity, charge=self.charge) self.molecule_psi4 = run_psi4( self.molecule_data, run_fci=True) # old version of openfermion # Hamiltonian transforms self.molecule_ham = self.molecule_psi4.get_molecular_hamiltonian() self.hf_energy = self.molecule_psi4.hf_energy.item( ) # old version of openfermion self.fci_energy = self.molecule_psi4.fci_energy.item( ) # old version of openfermion # TODO: the code below corresponds to the most recent version in the opefermion documentation. # However it has problems with ray??? # calculate_molecule_psi4 = run_pyscf(self.molecule_data, run_scf=True, run_cisd=True, run_fci=True) # self.molecule_ham = calculate_molecule_psi4.get_molecular_hamiltonian() # self.hf_energy = calculate_molecule_psi4.hf_energy # self.fci_energy = float(calculate_molecule_psi4.fci_energy) # del calculate_molecule_psi4 self.energy_eigenvalues = None # use this only if calculating excited states if frozen_els is None: self.n_electrons = n_electrons self.n_orbitals = n_orbitals self.n_qubits = n_orbitals self.fermion_ham = get_fermion_operator(self.molecule_ham) else: self.n_electrons = n_electrons - len(frozen_els['occupied']) self.n_orbitals = n_orbitals - len(frozen_els['occupied']) - len( frozen_els['unoccupied']) self.n_qubits = self.n_orbitals self.fermion_ham = freeze_orbitals( get_fermion_operator(self.molecule_ham), occupied=frozen_els['occupied'], unoccupied=frozen_els['unoccupied'], prune=True) self.jw_qubit_ham = jordan_wigner(self.fermion_ham) # this is used only for calculating excited states. list of [term_index, term_state] self.H_lower_state_terms = None
def test_fermionic_hamiltonian_from_integrals(self): constant = self.molecule.nuclear_repulsion doci_constant = constant hc, hr1, hr2 = get_doci_from_integrals( self.molecule.one_body_integrals, self.molecule.two_body_integrals) doci = DOCIHamiltonian(doci_constant, hc, hr1, hr2) doci_qubit_op = doci.qubit_operator doci_mat = get_sparse_operator(doci_qubit_op).toarray() #doci_eigvals, doci_eigvecs = numpy.linalg.eigh(doci_mat) doci_eigvals, _ = numpy.linalg.eigh(doci_mat) tensors = doci.n_body_tensors one_body_tensors, two_body_tensors = tensors[(1, 0)], tensors[(1, 1, 0, 0)] fermion_op1 = get_fermion_operator( InteractionOperator(constant, one_body_tensors, 0. * two_body_tensors)) fermion_op2 = get_fermion_operator( InteractionOperator(0, 0 * one_body_tensors, 0.5 * two_body_tensors)) import openfermion as of fermion_op1_jw = of.transforms.jordan_wigner(fermion_op1) fermion_op2_jw = of.transforms.jordan_wigner(fermion_op2) fermion_op_jw = fermion_op1_jw + fermion_op2_jw #fermion_eigvals, fermion_eigvecs = numpy.linalg.eigh( # get_sparse_operator(fermion_op_jw).toarray()) fermion_eigvals, _ = numpy.linalg.eigh( get_sparse_operator(fermion_op_jw).toarray()) for eigval in doci_eigvals: assert any(abs(fermion_eigvals - eigval) < 1e-6), "The DOCI spectrum should \ have been contained in the spectrum of the fermionic operators" fermion_diagonal = get_sparse_operator( fermion_op_jw).toarray().diagonal() qubit_diagonal = doci_mat.diagonal() assert numpy.isclose( fermion_diagonal[0], qubit_diagonal[0] ) and numpy.isclose( fermion_diagonal[-1], qubit_diagonal[-1] ), "The first and last elements of hte qubit and fermionic diagonal of \ the Hamiltonian maxtrix should be the same as the vaccum should be \ mapped to the computational all zero state and the completely filled \ state should be mapped to the all one state" print(fermion_diagonal) print(qubit_diagonal)
def transform_interaction_operator(transformation: str, input_operator: Union[str, SymbolicOperator]): """Transform an interaction operator through either the Bravyi-Kitaev or Jordan-Wigner transformations. The results are serialized into a JSON under the files: "transformed-operator.json" and "timing.json" ARGS: transformation (str): The transformation to use. Either "Jordan-Wigner" or "Bravyi-Kitaev" input_operator (Union[str, SymbolicOperator]): The interaction operator to transform """ if isinstance(input_operator, str): input_operator = load_interaction_operator(input_operator) if transformation == "Jordan-Wigner": transformation = jordan_wigner elif transformation == "Bravyi-Kitaev": input_operator = get_fermion_operator(input_operator) transformation = bravyi_kitaev else: raise RuntimeError("Unrecognized transformation ", transformation) start_time = time.time() transformed_operator = transformation(input_operator) walltime = time.time() - start_time save_qubit_operator(transformed_operator, "transformed-operator.json") save_timing(walltime, "timing.json")
def test_erpa_eom_ham_h2(): filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.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 = reduced_ham.one_body_tensor.shape[0] // 2 full_basis = {} # erpa basis. A, B basis in RPA language cnt = 0 for p, q in product(range(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 apply_fermionic_constraints(interaction_operator: InteractionOperator, number_of_particles: int) -> None: fermion_operator = get_fermion_operator( load_interaction_operator(interaction_operator)) result = apply_constraints(fermion_operator, number_of_particles) save_interaction_operator(get_interaction_operator(result), "output_operator.json")
def test_get_diagonal_component_polynomial_tensor(self): fermion_op = FermionOperator("0^ 1^ 2^ 0 1 2", 1.0) fermion_op += FermionOperator("0^ 1^ 2^ 0 1 3", 2.0) fermion_op += FermionOperator((), 3.0) polynomial_tensor = get_polynomial_tensor(fermion_op) diagonal_op, remainder_op = get_diagonal_component(polynomial_tensor) self.assertTrue((diagonal_op + remainder_op) == polynomial_tensor) diagonal_qubit_op = jordan_wigner(get_fermion_operator(diagonal_op)) remainder_qubit_op = jordan_wigner(get_fermion_operator(remainder_op)) for term in diagonal_qubit_op.terms: for pauli in term: self.assertTrue(pauli[1] == "Z") for term in remainder_qubit_op.terms: is_diagonal = True for pauli in term: if pauli[1] != "Z": is_diagonal = False break self.assertFalse(is_diagonal)
def test_spin_symmetric_bogoliubov_transform( n_spatial_orbitals, conserves_particle_number, atol=5e-5): n_qubits = 2*n_spatial_orbitals qubits = LineQubit.range(n_qubits) # Initialize a random quadratic Hamiltonian quad_ham = random_quadratic_hamiltonian( n_spatial_orbitals, conserves_particle_number, real=True, expand_spin=True, seed=28166) # Reorder the Hamiltonian and get sparse matrix quad_ham = openfermion.get_quadratic_hamiltonian( openfermion.reorder( openfermion.get_fermion_operator(quad_ham), openfermion.up_then_down) ) quad_ham_sparse = get_sparse_operator(quad_ham) # Compute the orbital energies and transformation_matrix up_orbital_energies, _, _ = ( quad_ham.diagonalizing_bogoliubov_transform(spin_sector=0)) down_orbital_energies, _, _ = ( quad_ham.diagonalizing_bogoliubov_transform(spin_sector=1)) _, transformation_matrix, _ = ( quad_ham.diagonalizing_bogoliubov_transform()) # Pick some orbitals to occupy up_orbitals = list(range(2)) down_orbitals = [0, 2, 3] energy = sum(up_orbital_energies[up_orbitals]) + sum( down_orbital_energies[down_orbitals]) + quad_ham.constant # Construct initial state initial_state = ( sum(2**(n_qubits - 1 - int(i)) for i in up_orbitals) + sum(2**(n_qubits - 1 - int(i+n_spatial_orbitals)) for i in down_orbitals) ) # Apply the circuit circuit = cirq.Circuit.from_ops( bogoliubov_transform( qubits, transformation_matrix, initial_state=initial_state)) state = circuit.apply_unitary_effect_to_state(initial_state) # Check that the result is an eigenstate with the correct eigenvalue numpy.testing.assert_allclose( quad_ham_sparse.dot(state), energy * state, atol=atol)
def __init__(self, oei: np.ndarray, tei: np.ndarray, operator_pool, n_alpha: int, n_beta: int, iter_max=30, verbose=True, stopping_epsilon=1.0E-3, delta_e_eps=1.0E-6): """ ADAPT-VQE object. Args: oei: one electron integrals in the spatial basis tei: two-electron integrals in the spatial basis operator_pool: Object with .op_pool that is a list of antihermitian FermionOperators n_alpha: Number of alpha-electrons n_beta: Number of beta-electrons iter_max: Maximum ADAPT-VQE steps to take verbose: Print the iteration information stopping_epsilon: define the <[G, H]> value that triggers stopping """ elec_hamil = RestrictedHamiltonian((oei, np.einsum("ijlk", -0.5 * tei))) soei, stei = spinorb_from_spatial(oei, tei) astei = np.einsum('ijkl', stei) - np.einsum('ijlk', stei) molecular_hamiltonian = InteractionOperator(0, soei, 0.25 * astei) reduced_ham = make_reduced_hamiltonian(molecular_hamiltonian, n_alpha + n_beta) self.reduced_ham = reduced_ham self.k2_ham = of.get_fermion_operator(reduced_ham) self.k2_fop = build_hamiltonian(self.k2_ham, elec_hamil.dim(), conserve_number=True) self.elec_hamil = elec_hamil self.iter_max = iter_max self.sdim = elec_hamil.dim() # change to use multiplicity to derive this for open shell self.nalpha = n_alpha self.nbeta = n_beta self.sz = self.nalpha - self.nbeta self.nele = self.nalpha + self.nbeta self.verbose = verbose self.operator_pool = operator_pool self.stopping_eps = stopping_epsilon self.delta_e_eps = delta_e_eps
def test_prepare_gaussian_state_with_spin_symmetry(n_spatial_orbitals, conserves_particle_number, occupied_orbitals, initial_state, atol=1e-5): n_qubits = 2 * n_spatial_orbitals qubits = LineQubit.range(n_qubits) # Initialize a random quadratic Hamiltonian quad_ham = random_quadratic_hamiltonian( n_spatial_orbitals, conserves_particle_number, real=True, expand_spin=True, seed=639) # Reorder the Hamiltonian and get sparse matrix quad_ham = openfermion.get_quadratic_hamiltonian( openfermion.reorder( openfermion.get_fermion_operator(quad_ham), openfermion.up_then_down) ) quad_ham_sparse = get_sparse_operator(quad_ham) # Compute the energy of the desired state energy = 0.0 for spin_sector in range(2): orbital_energies, _, _ = ( quad_ham.diagonalizing_bogoliubov_transform( spin_sector=spin_sector) ) energy += sum(orbital_energies[i] for i in occupied_orbitals[spin_sector]) energy += quad_ham.constant # Get the state using a circuit simulation circuit = cirq.Circuit.from_ops( prepare_gaussian_state( qubits, quad_ham, occupied_orbitals, initial_state=initial_state)) if isinstance(initial_state, list): initial_state = sum(1 << (n_qubits - 1 - i) for i in initial_state) state = circuit.apply_unitary_effect_to_state(initial_state) # Check that the result is an eigenstate with the correct eigenvalue numpy.testing.assert_allclose( quad_ham_sparse.dot(state), energy * state, atol=atol)
def get_molecular_Hamiltonian(geometry=[('H', (0.0, 0.0, 0.0)), ('H', (0.0, 0.0, 0.7414))], basis='sto-3g', multiplicity=1, charge=0): # Perform electronic structure calculations and obtain Hamiltonian as an InteractionOperator hamiltonian = ofpyscf.generate_molecular_hamiltonian( geometry, basis, multiplicity, charge) # Convert to a FermionOperator hamiltonian_ferm_op = of.get_fermion_operator(hamiltonian) mol_qubit_hamiltonianBK = bravyi_kitaev( hamiltonian_ferm_op ) # JW is exponential in the maximum locality of the original FermionOperator. mol_matrix = get_sparse_operator(mol_qubit_hamiltonianBK).todense() return mol_matrix
def transform_interaction_operator(transformation, input_operator): input_operator = load_interaction_operator(input_operator) if transformation == "Jordan-Wigner": transformation = jordan_wigner elif transformation == "Bravyi-Kitaev": input_operator = get_fermion_operator(input_operator) transformation = bravyi_kitaev else: raise RuntimeError("Unrecognized transformation ", transformation) start_time = time.time() transformed_operator = transformation(input_operator) walltime = time.time() - start_time save_qubit_operator(transformed_operator, "transformed-operator.json") save_timing(walltime, "timing.json")
def diagonal_coulomb_potential_and_kinetic_terms_as_arrays(hamiltonian): """Give the potential and kinetic terms of a diagonal Coulomb Hamiltonian as arrays. Args: hamiltonian (FermionOperator): The diagonal Coulomb Hamiltonian to separate the potential and kinetic terms for. Identity is arbitrarily chosen to be part of the potential. Returns: Tuple of (potential_terms, kinetic_terms). Both elements of the tuple are numpy arrays of FermionOperators. """ if not isinstance(hamiltonian, FermionOperator): try: hamiltonian = normal_ordered(get_fermion_operator(hamiltonian)) except TypeError: raise TypeError('hamiltonian must be either a FermionOperator ' 'or DiagonalCoulombHamiltonian.') potential = FermionOperator.zero() kinetic = FermionOperator.zero() for term, coeff in iteritems(hamiltonian.terms): acted = set(term[i][0] for i in range(len(term))) if len(acted) == len(term) / 2: potential += FermionOperator(term, coeff) else: kinetic += FermionOperator(term, coeff) potential_terms = numpy.array([ FermionOperator(term, coeff) for term, coeff in iteritems(potential.terms) ]) kinetic_terms = numpy.array([ FermionOperator(term, coeff) for term, coeff in iteritems(kinetic.terms) ]) return (potential_terms, kinetic_terms)
# limitations under the License. import numpy import pytest import cirq import openfermion from openfermion import random_diagonal_coulomb_hamiltonian from openfermioncirq import HamiltonianObjective # Construct a Hamiltonian for testing test_hamiltonian = random_diagonal_coulomb_hamiltonian(4, real=True, seed=26191) test_fermion_op = openfermion.get_fermion_operator(test_hamiltonian) def test_hamiltonian_objective_value(): obj = HamiltonianObjective(test_hamiltonian) obj_linear_op = HamiltonianObjective(test_hamiltonian, use_linear_op=True) hamiltonian_sparse = openfermion.get_sparse_operator(test_hamiltonian) simulator = cirq.google.XmonSimulator() qubits = cirq.LineQubit.range(4) numpy.random.seed(10581) result = simulator.simulate(cirq.testing.random_circuit(qubits, 5, 0.8), qubit_order=qubits) correct_val = openfermion.expectation(hamiltonian_sparse, result.final_state)
def test_first_factorization(): molecule = build_lih_moleculardata() oei, tei = molecule.get_integrals() lrt_obj = LowRankTrotter(oei=oei, tei=tei) ( eigenvalues, one_body_squares, one_body_correction, ) = lrt_obj.first_factorization() # get true op n_qubits = molecule.n_qubits fermion_tei = of.FermionOperator() test_tei_tensor = np.zeros((n_qubits, n_qubits, n_qubits, n_qubits)) for p, q, r, s in product(range(oei.shape[0]), repeat=4): if np.abs(tei[p, q, r, s]) < EQ_TOLERANCE: coefficient = 0.0 else: coefficient = tei[p, q, r, s] / 2.0 for sigma, tau in product(range(2), repeat=2): if 2 * p + sigma == 2 * q + tau or 2 * r + tau == 2 * s + sigma: continue term = ( (2 * p + sigma, 1), (2 * q + tau, 1), (2 * r + tau, 0), (2 * s + sigma, 0), ) fermion_tei += of.FermionOperator(term, coefficient=coefficient) test_tei_tensor[2 * p + sigma, 2 * q + tau, 2 * r + tau, 2 * s + sigma] = coefficient mol_ham = of.InteractionOperator( one_body_tensor=np.zeros((n_qubits, n_qubits)), two_body_tensor=test_tei_tensor, constant=0, ) # check induced norm on operator checked_op = of.FermionOperator() for ( p, q, ) in product(range(n_qubits), repeat=2): term = ((p, 1), (q, 0)) coefficient = one_body_correction[p, q] checked_op += of.FermionOperator(term, coefficient) # Build back two-body component. for l in range(one_body_squares.shape[0]): one_body_operator = of.FermionOperator() for p, q in product(range(n_qubits), repeat=2): term = ((p, 1), (q, 0)) coefficient = one_body_squares[l, p, q] one_body_operator += of.FermionOperator(term, coefficient) checked_op += eigenvalues[l] * (one_body_operator**2) true_fop = of.normal_ordered(of.get_fermion_operator(mol_ham)) difference = of.normal_ordered(checked_op - true_fop) assert np.isclose(0, difference.induced_norm())
# dummy geometry geometry = [["Li", [0, 0, 0], ["H", [0, 0, 1.4]]]] charge = 0 multiplicity = 1 molecule = of.MolecularData( geometry=geometry, basis="sto-3g", charge=charge, multiplicity=multiplicity, ) molecule.one_body_integrals = h1e molecule.two_body_integrals = np.einsum("ijlk", -2 * h2e) molecular_hamiltonian = molecule.get_molecular_hamiltonian() molecular_hamiltonian.constant = 0 ham_fop = of.get_fermion_operator(molecular_hamiltonian) ham_mat = of.get_sparse_operator(of.jordan_wigner(ham_fop)).toarray() cirq_ci = fqe.to_cirq(wfn) cirq_ci = cirq_ci.reshape((2**12, 1)) assert np.isclose(cirq_ci.conj().T @ ham_mat @ cirq_ci, ecalc) hf_idx = int("111100000000", 2) hf_idx2 = int("111001000000", 2) hf_vec = np.zeros((2**12, 1)) hf_vec2 = np.zeros((2**12, 1)) hf_vec[hf_idx, 0] = 1.0 hf_vec2[hf_idx2, 0] = 1.0 # scale diagonal so vacuum has non-zero energy ww, vv = davidsonliu(ham_mat + np.eye(ham_mat.shape[0]),