def decompose(hf_file, mapping="jordan_wigner", core=None, active=None): r"""Decomposes the molecular Hamiltonian into a linear combination of Pauli operators using OpenFermion tools. This function uses OpenFermion functions to build the second-quantized electronic Hamiltonian of the molecule and map it to the Pauli basis using the Jordan-Wigner or Bravyi-Kitaev transformation. Args: hf_file (str): absolute path to the hdf5-formatted file with the Hartree-Fock electronic structure mapping (str): Specifies the transformation to map the fermionic Hamiltonian to the Pauli basis. Input values can be ``'jordan_wigner'`` or ``'bravyi_kitaev'``. core (list): indices of core orbitals, i.e., the orbitals that are not correlated in the many-body wave function active (list): indices of active orbitals, i.e., the orbitals used to build the correlated many-body wave function Returns: QubitOperator: an instance of OpenFermion's ``QubitOperator`` **Example** >>> decompose('./pyscf/sto-3g/h2', mapping='bravyi_kitaev') (-0.04207897696293986+0j) [] + (0.04475014401986122+0j) [X0 Z1 X2] + (0.04475014401986122+0j) [X0 Z1 X2 Z3] +(0.04475014401986122+0j) [Y0 Z1 Y2] + (0.04475014401986122+0j) [Y0 Z1 Y2 Z3] +(0.17771287459806262+0j) [Z0] + (0.17771287459806265+0j) [Z0 Z1] +(0.1676831945625423+0j) [Z0 Z1 Z2] + (0.1676831945625423+0j) [Z0 Z1 Z2 Z3] +(0.12293305054268105+0j) [Z0 Z2] + (0.12293305054268105+0j) [Z0 Z2 Z3] +(0.1705973832722409+0j) [Z1] + (-0.2427428049645989+0j) [Z1 Z2 Z3] +(0.1762764080276107+0j) [Z1 Z3] + (-0.2427428049645989+0j) [Z2] """ # loading HF data from the hdf5 file molecule = MolecularData(filename=hf_file.strip()) # getting the terms entering the second-quantized Hamiltonian terms_molecular_hamiltonian = molecule.get_molecular_hamiltonian( occupied_indices=core, active_indices=active ) # generating the fermionic Hamiltonian fermionic_hamiltonian = get_fermion_operator(terms_molecular_hamiltonian) mapping = mapping.strip().lower() if mapping not in ("jordan_wigner", "bravyi_kitaev"): raise TypeError( "The '{}' transformation is not available. \n " "Please set 'mapping' to 'jordan_wigner' or 'bravyi_kitaev'.".format(mapping) ) # fermionic-to-qubit transformation of the Hamiltonian if mapping == "bravyi_kitaev": return bravyi_kitaev(fermionic_hamiltonian) return jordan_wigner(fermionic_hamiltonian)
def build_lih_moleculardata() -> MolecularData: """Returns LiH molecular data.""" geometry = [("Li", (0.0, 0.0, 0.0)), ("H", (0.0, 0.0, 1.45))] basis = "sto-3g" multiplicity = 1 filename = os.path.join( fud.__file__.replace("__init__.py", ""), "H1-Li1_sto-3g_singlet_1.45.hdf5", ) molecule = MolecularData(geometry, basis, multiplicity, filename=filename) molecule.load() return molecule
def _generate_molecule(self, p: IParameter) -> MolecularData: """Produce molecule that can be used by the hamiltonian. Using a singlet state with S = 0 to specify we are looking for the lowest singlet energy state. multiplicity = 2S + 1 """ geometry = [('H', (0., 0., 0.)), ('H', (0., 0., p['r0']))] basis = 'sto-3g' multiplicity = 1 charge = 0 description = str(p['r0']) # Change to whatever directory you want cwd = os.getcwd() data_directory = cwd+'/mol_data' if not os.path.exists(data_directory): os.mkdir(data_directory) filename = data_directory+'/H2_'+description run_scf = 1 run_mp2 = 1 run_cisd = 1 run_ccsd = 1 run_fci = 1 delete_input = False delete_output = False verbose = False molecule = MolecularData( geometry, basis, multiplicity, description=description, filename=filename) if os.path.exists('{}.hdf5'.format(filename)): molecule.load() else: molecule = run_psi4(molecule, verbose=verbose, run_scf=run_scf, run_mp2=run_mp2, run_cisd=run_cisd, run_ccsd=run_ccsd, run_fci=run_fci) # print(f'Reference (Full Configuration Energy: {molecule.fci_energy})') return molecule
def build_h4square_moleculardata() -> MolecularData: """Returns H4 molecular data.""" geometry = [ ("H", [0.5, 0.5, 0]), ("H", [0.5, -0.5, 0]), ("H", [-0.5, 0.5, 0]), ("H", [-0.5, -0.5, 0]), ] basis = "sto-3g" multiplicity = 1 filename = os.path.join(fud.__file__.replace("__init__.py", ""), "H4_sto-3g_singlet.hdf5") molecule = MolecularData(geometry, basis, multiplicity, filename=filename) molecule.load() return molecule
def generate_molecular_hamiltonian_mod(guess, geometry, basis, multiplicity, charge=0, n_active_electrons=None, n_active_orbitals=None): """Function Old subroutine to get molecular hamiltonian by using pyscf. Author(s): Takashi Tsuchimochi """ # Run electronic structure calculations molecule, pyscf_mol \ = run_pyscf_mod(guess, n_active_orbitals, n_active_electrons, MolecularData(geometry, basis, multiplicity, charge, data_directory=cf.input_dir)) # Freeze core orbitals and truncate to active space if n_active_electrons is None: n_core_orbitals = 0 occupied_indices = None else: n_core_orbitals = (molecule.n_electrons-n_active_electrons)//2 occupied_indices = list(range(n_core_orbitals)) if n_active_orbitals is None: active_indices = None else: active_indices = list(range(n_core_orbitals, n_core_orbitals+n_active_orbitals)) return molecule.get_molecular_hamiltonian(occupied_indices=occupied_indices, active_indices=active_indices)
def test_rhf_min(): filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.hdf5") molecule = MolecularData(filename=filename) overlap = molecule.overlap_integrals mo_obi = molecule.one_body_integrals mo_tbi = molecule.two_body_integrals rotation_mat = molecule.canonical_orbitals.T.dot(overlap) obi = general_basis_change(mo_obi, rotation_mat, (1, 0)) tbi = general_basis_change(mo_tbi, rotation_mat, (1, 1, 0, 0)) hff = HartreeFockFunctional(one_body_integrals=obi, two_body_integrals=tbi, overlap=overlap, n_electrons=molecule.n_electrons, model='rhf', nuclear_repulsion=molecule.nuclear_repulsion) result = rhf_minimization(hff) assert isinstance(result, OptimizeResult) result2 = rhf_minimization(hff, initial_guess=np.array([0]), sp_options={ 'maxiter': 100, 'disp': False }) assert isinstance(result2, OptimizeResult) assert np.isclose(result2.fun, result.fun)
def vqe_fixed(vqe_strategy, vqe_tomography, vqe_method): """ Initialize a VQE experiment with a custom hamiltonian given as constant input """ _vqe = None if vqe_strategy == "custom_program": custom_ham = PauliSum([PauliTerm(*x) for x in HAMILTONIAN]) _vqe = VQEexperiment(hamiltonian=custom_ham, method=vqe_method, strategy=vqe_strategy, parametric=False, tomography=vqe_tomography, shotN=NSHOTS_INT) elif vqe_strategy == "UCCSD": cwd = os.path.abspath(os.path.dirname(__file__)) fname = os.path.join(cwd, "resources", "H2.hdf5") molecule = MolecularData(filename=fname) _vqe = VQEexperiment(molecule=molecule, method=vqe_method, strategy=vqe_strategy, parametric=False, tomography=vqe_tomography, shotN=NSHOTS_INT) return _vqe
def get_molecule(element_names, bond_len): geometry = [[element_names[0], [0, 0, 0]], [element_names[1], [0, 0, bond_len]]] basis = 'sto-3g' multiplicity = 1 description = '{:.2}'.format(bond_len) data_directory = DATA_DIRECTORY molecule = MolecularData(geometry, basis, multiplicity, description=description, data_directory=data_directory) molecule.load() return molecule
def test_d2_to_g2(): # this should test for correctness filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5") molecule = MolecularData(filename=filename) molecule.load() opdm = molecule.fci_one_rdm tpdm = molecule.fci_two_rdm phdm = map_two_pdm_to_particle_hole_dm(tpdm, opdm) db = tpdm_to_phdm_mapping(molecule.n_qubits) topdm = Tensor(tensor=opdm, name='ck') ttpdm = Tensor(tensor=np.einsum('ijlk', tpdm), name='cckk') tphdm = Tensor(tensor=np.einsum('ijlk', phdm), name='ckck') mt = MultiTensor(tensors=[topdm, ttpdm, tphdm], dual_basis=db) A, _, b = mt.synthesize_dual_basis() vec_rdms = mt.vectorize_tensors() assert np.isclose(np.linalg.norm(A.dot(vec_rdms) - b), 0)
def test_table_two_particle(name, core, active, v_op_exp): r"""Test the FermionOperator built by the function `two_particle` of the `obs` module""" hf_data = MolecularData(filename=os.path.join(ref_dir, name)) v_op = qchem.two_particle(hf_data.two_body_integrals, core=core, active=active) assert v_op.terms == v_op_exp
def test_table_one_particle(core, active, t_op_exp): r"""Test the correctness of the FermionOperator built by the `'one_particle'` function of the `obs` module""" hf = MolecularData(filename=os.path.join(ref_dir, "h2o_psi4")) t_op = qchem.one_particle(hf.one_body_integrals, core=core, active=active) assert t_op.terms == t_op_exp
def test_gradient_lih(): filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5") molecule = MolecularData(filename=filename) overlap = molecule.overlap_integrals mo_obi = molecule.one_body_integrals mo_tbi = molecule.two_body_integrals rotation_mat = molecule.canonical_orbitals.T.dot(overlap) obi = general_basis_change(mo_obi, rotation_mat, (1, 0)) tbi = general_basis_change(mo_tbi, rotation_mat, (1, 1, 0, 0)) hff = HartreeFockFunctional(one_body_integrals=obi, two_body_integrals=tbi, overlap=overlap, n_electrons=molecule.n_electrons, model='rhf', nuclear_repulsion=molecule.nuclear_repulsion) params = np.random.randn(hff.nocc * hff.nvirt) u = sp.linalg.expm( rhf_params_to_matrix(params, hff.num_orbitals, occ=hff.occ, virt=hff.virt)) grad_dim = hff.nocc * hff.nvirt initial_opdm = np.diag([1] * hff.nocc + [0] * hff.nvirt) final_opdm = u.dot(initial_opdm).dot(u.conj().T) grad = hff.rhf_global_gradient(params, final_opdm) # get finite difference gradient finite_diff_grad = np.zeros(grad_dim) epsilon = 0.0001 for i in range(grad_dim): params_epsilon = params.copy() params_epsilon[i] += epsilon u = sp.linalg.expm( rhf_params_to_matrix(params_epsilon, hff.num_orbitals, occ=hff.occ, virt=hff.virt)) tfinal_opdm = u.dot(initial_opdm).dot(u.conj().T) energy_plus_epsilon = hff.energy_from_rhf_opdm(tfinal_opdm) params_epsilon[i] -= 2 * epsilon u = sp.linalg.expm( rhf_params_to_matrix(params_epsilon, hff.num_orbitals, occ=hff.occ, virt=hff.virt)) tfinal_opdm = u.dot(initial_opdm).dot(u.conj().T) energy_minus_epsilon = hff.energy_from_rhf_opdm(tfinal_opdm) finite_diff_grad[i] = (energy_plus_epsilon - energy_minus_epsilon) / (2 * epsilon) assert np.allclose(finite_diff_grad, grad, atol=epsilon)
def test_generate_hamiltonian(): filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5") molecule = MolecularData(filename=filename) mo_obi = molecule.one_body_integrals mo_tbi = molecule.two_body_integrals mol_ham = generate_hamiltonian(mo_obi, mo_tbi, constant=0) assert isinstance(mol_ham, InteractionOperator) assert np.allclose(mol_ham.one_body_tensor[::2, ::2], mo_obi) assert np.allclose(mol_ham.one_body_tensor[1::2, 1::2], mo_obi) assert np.allclose(mol_ham.two_body_tensor[::2, ::2, ::2, ::2], 0.5 * mo_tbi) assert np.allclose(mol_ham.two_body_tensor[1::2, 1::2, 1::2, 1::2], 0.5 * mo_tbi)
def test_inconsistent_active_spaces(mol_name, act_electrons, act_orbitals, message_match): r"""Test that an error is raised if an inconsistent active space is generated""" molecule = MolecularData(filename=os.path.join(ref_dir, mol_name)) with pytest.raises(ValueError, match=message_match): qchem.active_space( molecule.n_electrons, molecule.n_orbitals, mult=molecule.multiplicity, active_electrons=act_electrons, active_orbitals=act_orbitals, )
def test_moleculardata_to_restricted_hamiltonian(self): """ Convert an OpenFermion MolecularData object to a fqe.RestrictedHamiltonian """ h1e, h2e, _ = build_lih_data('energy') # dummy geometry geometry = [['Li', [0, 0, 0], ['H', [0, 0, 1.4]]]] charge = 0 multiplicity = 1 molecule = MolecularData(geometry=geometry, basis='sto-3g', charge=charge, multiplicity=multiplicity) molecule.one_body_integrals = h1e molecule.two_body_integrals = numpy.einsum('ijlk', -2 * h2e) molecule.nuclear_repulsion = 0 restricted_ham = openfermion_utils.molecular_data_to_restricted_fqe_op( molecule=molecule) h1e_test, h2e_test = restricted_ham.tensors() self.assertTrue(numpy.allclose(h1e_test, h1e)) self.assertTrue(numpy.allclose(h2e_test, h2e))
def generate_molecular_hamiltonian( geometry, basis, multiplicity, charge=0, n_active_electrons=None, n_active_orbitals=None): """Generate a molecular Hamiltonian with the given properties. Args: geometry: A list of tuples giving the coordinates of each atom. An example is [('H', (0, 0, 0)), ('H', (0, 0, 0.7414))]. Distances in angstrom. Use atomic symbols to specify atoms. basis: A string giving the basis set. An example is 'cc-pvtz'. Only optional if loading from file. multiplicity: An integer giving the spin multiplicity. charge: An integer giving the charge. n_active_electrons: An optional integer specifying the number of electrons desired in the active space. n_active_orbitals: An optional integer specifying the number of spatial orbitals desired in the active space. Returns: The Hamiltonian as an InteractionOperator. """ # Run electronic structure calculations molecule = run_pyscf( MolecularData(geometry, basis, multiplicity, charge) ) # Freeze core orbitals and truncate to active space if n_active_electrons is None: n_core_orbitals = 0 occupied_indices = None else: n_core_orbitals = (molecule.n_electrons - n_active_electrons) // 2 occupied_indices = list(range(n_core_orbitals)) if n_active_orbitals is None: active_indices = None else: active_indices = list(range(n_core_orbitals, n_core_orbitals + n_active_orbitals)) return molecule.get_molecular_hamiltonian( occupied_indices=occupied_indices, active_indices=active_indices)
def test_active_spaces(mol_name, act_electrons, act_orbitals, core_ref, active_ref): r"""Test the correctness of the generated active spaces""" molecule = MolecularData(filename=os.path.join(ref_dir, mol_name)) core, active = qchem.active_space( molecule.n_electrons, molecule.n_orbitals, mult=molecule.multiplicity, active_electrons=act_electrons, active_orbitals=act_orbitals, ) assert core == core_ref assert active == active_ref
def test_hf_calculations(package, tmpdir, psi4_support, tol): r"""Test the correctness of the HF calculation""" if package == "Psi4" and not psi4_support: pytest.skip("Skipped, no Psi4 support") n_atoms = 2 n_electrons = 2 n_orbitals = 2 hf_energy = -1.1173490350703152 orbital_energies = np.array([-0.59546347, 0.71416528]) one_body_integrals = np.array([[-1.27785300e00, 1.11022302e-16], [0.00000000e00, -4.48299698e-01]]) two_body_integrals = np.array([ [ [[6.82389533e-01, 0.00000000e00], [6.93889390e-17, 1.79000576e-01]], [[4.16333634e-17, 1.79000576e-01], [6.70732778e-01, 0.00000000e00]], ], [ [[5.55111512e-17, 6.70732778e-01], [1.79000576e-01, 1.11022302e-16]], [[1.79000576e-01, 0.00000000e00], [2.77555756e-17, 7.05105632e-01]], ], ]) fullpath = qchem.meanfield(symbols, coordinates, name=name, package=package, outpath=tmpdir.strpath) molecule = MolecularData(filename=fullpath) assert molecule.n_atoms == n_atoms assert molecule.n_electrons == n_electrons assert molecule.n_orbitals == n_orbitals assert np.allclose(molecule.hf_energy, hf_energy, **tol) assert np.allclose(molecule.orbital_energies, orbital_energies, **tol) assert np.allclose(molecule.one_body_integrals, one_body_integrals, **tol) assert np.allclose(molecule.two_body_integrals, two_body_integrals, **tol)
def test_rhf_func_generator(): filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5") molecule = MolecularData(filename=filename) overlap = molecule.overlap_integrals mo_obi = molecule.one_body_integrals mo_tbi = molecule.two_body_integrals rotation_mat = molecule.canonical_orbitals.T.dot(overlap) obi = general_basis_change(mo_obi, rotation_mat, (1, 0)) tbi = general_basis_change(mo_tbi, rotation_mat, (1, 1, 0, 0)) hff = HartreeFockFunctional(one_body_integrals=obi, two_body_integrals=tbi, overlap=overlap, n_electrons=molecule.n_electrons, model='rhf', nuclear_repulsion=molecule.nuclear_repulsion) unitary, energy, gradient = rhf_func_generator(hff) assert isinstance(unitary, Callable) assert isinstance(energy, Callable) assert isinstance(gradient, Callable) params = np.random.randn(hff.nocc * hff.nvirt) u = unitary(params) assert np.allclose(u.conj().T.dot(u), np.eye(hff.num_orbitals)) assert isinstance(energy(params), float) assert isinstance(gradient(params), np.ndarray) _, _, _, opdm_func = rhf_func_generator(hff, get_opdm_func=True) assert isinstance(opdm_func, Callable) assert isinstance(opdm_func(params), np.ndarray) assert np.isclose(opdm_func(params).shape[0], hff.num_orbitals) _, energy, _ = rhf_func_generator(hff, init_occ_vec=np.array([1, 1, 1, 1, 0, 0])) assert isinstance(energy(params), float)
) print(" VQD") print( " --------------------------------------------------------------------------" ) molecules = [] SymbolicAnsatz = None for point in [3.0]: #range(1, n_points + 1): bond_length = point #bond_length_interval * float(point) + 0.2 geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., bond_length))] MolecularMeta = {} molecule = MolecularData(geometry, basis, multiplicity, description=str(round(bond_length, 2))) molecule = run_pyscf(molecule, run_scf=run_scf) # Basic information jim = molecule.name print("===== Molecule {} =====".format(jim)) MolecularMeta['Name'] = jim MolecularMeta['Geo'] = bond_length # Hamiltonian ham_qubit = jordan_wigner(molecule.get_molecular_hamiltonian()) ham_qubit.compress() # Now in QubitOperator form ham_sparse = qubit_operator_sparse(ham_qubit, n_qubits=molecule.n_qubits) print(' HF:', molecule.hf_energy)
def molecular_hamiltonian( name, geo_file, charge=0, mult=1, basis="sto-3g", package="pyscf", active_electrons=None, active_orbitals=None, mapping="jordan_wigner", outpath=".", wires=None, ): # pylint:disable=too-many-arguments r"""Generates the qubit Hamiltonian of a molecule. This function drives the construction of the second-quantized electronic Hamiltonian of a molecule and its transformation to the basis of Pauli matrices. #. The process begins by reading the file containing the geometry of the molecule. #. OpenFermion-PySCF or OpenFermion-Psi4 plugins are used to launch the Hartree-Fock (HF) calculation for the polyatomic system using the quantum chemistry package ``PySCF`` or ``Psi4``, respectively. - The net charge of the molecule can be given to simulate cationic/anionic systems. Also, the spin multiplicity can be input to determine the number of unpaired electrons occupying the HF orbitals as illustrated in the left panel of the figure below. - The basis of Gaussian-type *atomic* orbitals used to represent the *molecular* orbitals can be specified to go beyond the minimum basis approximation. Basis set availability per element can be found `here <www.psicode.org/psi4manual/master/basissets_byelement.html#apdx-basiselement>`_ #. An active space can be defined for a given number of *active electrons* occupying a reduced set of *active orbitals* in the vicinity of the frontier orbitals as sketched in the right panel of the figure below. #. Finally, the second-quantized Hamiltonian is mapped to the Pauli basis and converted to a PennyLane observable. | .. figure:: ../../_static/qchem/fig_mult_active_space.png :align: center :width: 90% | Args: name (str): name of the molecule geo_file (str): file containing the geometry of the molecule charge (int): Net charge of the molecule. If not specified a a neutral system is assumed. mult (int): Spin multiplicity :math:`\mathrm{mult}=N_\mathrm{unpaired} + 1` for :math:`N_\mathrm{unpaired}` unpaired electrons occupying the HF orbitals. Possible values of ``mult`` are :math:`1, 2, 3, \ldots`. If not specified, a closed-shell HF state is assumed. basis (str): Atomic basis set used to represent the molecular orbitals. Basis set availability per element can be found `here <www.psicode.org/psi4manual/master/basissets_byelement.html#apdx-basiselement>`_ package (str): quantum chemistry package (pyscf or psi4) used to solve the mean field electronic structure problem active_electrons (int): Number of active electrons. If not specified, all electrons are considered to be active. active_orbitals (int): Number of active orbitals. If not specified, all orbitals are considered to be active. mapping (str): transformation (``'jordan_wigner'`` or ``'bravyi_kitaev'``) used to map the fermionic Hamiltonian to the qubit Hamiltonian outpath (str): path to the directory containing output files wires (Wires, list, tuple, dict): Custom wire mapping for connecting to Pennylane ansatz. For types Wires/list/tuple, each item in the iterable represents a wire label corresponding to the qubit number equal to its index. For type dict, only int-keyed dict (for qubit-to-wire conversion) is accepted for partial mapping. If None, will use identity map. Returns: tuple[pennylane.Hamiltonian, int]: the fermionic-to-qubit transformed Hamiltonian and the number of qubits **Example** >>> name = "h2" >>> geo_file = "h2.xyz" >>> H, qubits = molecular_hamiltonian(name, geo_file) >>> print(qubits) 4 >>> print(H) (-0.04207897647782188) [I0] + (0.17771287465139934) [Z0] + (0.1777128746513993) [Z1] + (-0.24274280513140484) [Z2] + (-0.24274280513140484) [Z3] + (0.17059738328801055) [Z0 Z1] + (0.04475014401535161) [Y0 X1 X2 Y3] + (-0.04475014401535161) [Y0 Y1 X2 X3] + (-0.04475014401535161) [X0 X1 Y2 Y3] + (0.04475014401535161) [X0 Y1 Y2 X3] + (0.12293305056183801) [Z0 Z2] + (0.1676831945771896) [Z0 Z3] + (0.1676831945771896) [Z1 Z2] + (0.12293305056183801) [Z1 Z3] + (0.176276408043196) [Z2 Z3] """ geometry = read_structure(geo_file, outpath) hf_file = meanfield(name, geometry, charge, mult, basis, package, outpath) molecule = MolecularData(filename=hf_file) core, active = active_space( molecule.n_electrons, molecule.n_orbitals, mult, active_electrons, active_orbitals ) h_of, qubits = (decompose(hf_file, mapping, core, active), 2 * len(active)) return convert_observable(h_of, wires=wires), qubits
basis = 'sto-3g' # Orbital basis set multiplicity = 1 # Spin of reference determinant n_points = 60 # Number of geometries bond_length_interval = 4.0 / n_points run_scf = 1 run_ccsd = 1 molecules = [] for point in range(1, n_points + 1): bond_length = bond_length_interval * float(point) + 0.2 geometry = [('H', (0., 0., 0.)), ('Li', (0., 0., bond_length))] MolecularMeta = {} molecule = MolecularData(geometry, basis, multiplicity, description=str(round(bond_length, 2))) molecule = run_pyscf(molecule, run_scf=run_scf) MolecularMeta['Geo'] = bond_length MolecularMeta['FCI eigenstates'] = run_FCI(molecule, roots=8) molecules.append(MolecularMeta) with open('FCI_H1-Li1_sto-3g.json', 'w') as json_file: json.dump(molecules, json_file)
def meanfield( name, geometry, charge=0, mult=1, basis="sto-3g", package="pyscf", outpath="." ): # pylint: disable=too-many-arguments r"""Generates a file from which the mean field electronic structure of the molecule can be retrieved. This function uses OpenFermion-PySCF and OpenFermion-Psi4 plugins to perform the Hartree-Fock (HF) calculation for the polyatomic system using the quantum chemistry packages ``PySCF`` and ``Psi4``, respectively. The mean field electronic structure is saved in an hdf5-formatted file in the directory ``os.path.join(outpath, package, basis)``. The charge of the molecule can be given to simulate cationic/anionic systems. Also, the spin multiplicity can be input to determine the number of unpaired electrons occupying the HF orbitals as illustrated in the figure below. | .. figure:: ../../_static/qchem/hf_references.png :align: center :width: 50% | Args: name (str): molecule label geometry (list): list containing the symbol and Cartesian coordinates for each atom charge (int): net charge of the system mult (int): Spin multiplicity :math:`\mathrm{mult}=N_\mathrm{unpaired} + 1` for :math:`N_\mathrm{unpaired}` unpaired electrons occupying the HF orbitals. Possible values for ``mult`` are :math:`1, 2, 3, \ldots`. If not specified, a closed-shell HF state is assumed. basis (str): Atomic basis set used to represent the HF orbitals. Basis set availability per element can be found `here <www.psicode.org/psi4manual/master/basissets_byelement.html#apdx-basiselement>`_ package (str): Quantum chemistry package used to solve the Hartree-Fock equations. Either ``'pyscf'`` or ``'psi4'`` can be used. outpath (str): path to output directory Returns: str: absolute path to the file containing the mean field electronic structure **Example** >>> name = 'h2' >>> geometry = [['H', (0.0, 0.0, -0.35)], ['H', (0.0, 0.0, 0.35)]] >>> meanfield(name, geometry) ./pyscf/sto-3g/h2 """ package = package.strip().lower() if package not in ("psi4", "pyscf"): error_message = ( "Integration with quantum chemistry package '{}' is not available. \n Please set" " 'package' to 'pyscf' or 'psi4'.".format(package) ) raise TypeError(error_message) package_dir = os.path.join(outpath.strip(), package) basis_dir = os.path.join(package_dir, basis.strip()) if not os.path.isdir(package_dir): os.mkdir(package_dir) os.mkdir(basis_dir) elif not os.path.isdir(basis_dir): os.mkdir(basis_dir) path_to_file = os.path.join(basis_dir, name.strip()) molecule = MolecularData(geometry, basis, mult, charge, filename=path_to_file) if package == "psi4": run_psi4(molecule, run_scf=1, verbose=0, tolerate_error=1) if package == "pyscf": run_pyscf(molecule, run_scf=1, verbose=0) return path_to_file
def get_ham_from_psi4( wfn, mints, ndocc=None, nact=None, nuclear_repulsion_energy=0, ): """Get a molecular Hamiltonian from a Psi4 calculation. Args: wfn (psi4.core.Wavefunction): Psi4 wavefunction object mints (psi4.core.MintsHelper): Psi4 molecular integrals helper ndocc (int): number of doubly occupied molecular orbitals to include in the saved Hamiltonian. nact (int): number of active molecular orbitals to include in the saved Hamiltonian. nuclear_repulsion_energy (float): The ion-ion interaction energy. Returns: hamiltonian (openfermion.ops.InteractionOperator): the electronic Hamiltonian. """ assert wfn.same_a_b_orbs(), ( "Extraction of Hamiltonian from wavefunction" + "with different alpha and beta orbitals not yet supported :(" ) orbitals = wfn.Ca().to_array(dense=True) if nact is None and ndocc is None: trf_mat = orbitals ndocc = 0 nact = orbitals.shape[1] print( f"Active space selection options were reset to: ndocc = {ndocc} and nact = {nact}" ) elif nact is not None and ndocc is None: assert nact <= orbitals.shape[1] ndocc = 0 trf_mat = orbitals[:, :nact] print( f"Active space selection options were reset to: ndocc = {ndocc} and nact = {nact}" ) elif ndocc is not None and nact is None: assert ndocc <= orbitals.shape[1] nact = orbitals.shape[1] - ndocc trf_mat = orbitals print( f"Active space selection options were reset to: ndocc = {ndocc} and nact = {nact}" ) else: assert orbitals.shape[1] >= nact + ndocc trf_mat = orbitals[:, : nact + ndocc] # Note: code refactored to use Psi4 integral-transformation routines # no more storing the whole two-electron integral tensor when only an # active space is needed one_body_integrals = general_basis_change( np.asarray(mints.ao_kinetic()), orbitals, (1, 0) ) one_body_integrals += general_basis_change( np.asarray(mints.ao_potential()), orbitals, (1, 0) ) # Build the transformation matrices, i.e. the orbitals for which # we want the integrals, as Psi4.core.Matrix objects trf_mat = psi4.core.Matrix.from_array(trf_mat) two_body_integrals = np.asarray(mints.mo_eri(trf_mat, trf_mat, trf_mat, trf_mat)) n_orbitals = trf_mat.shape[1] two_body_integrals.reshape((n_orbitals, n_orbitals, n_orbitals, n_orbitals)) two_body_integrals = np.einsum("psqr", two_body_integrals) # Truncate one_body_integrals[np.absolute(one_body_integrals) < EQ_TOLERANCE] = 0.0 two_body_integrals[np.absolute(two_body_integrals) < EQ_TOLERANCE] = 0.0 occupied_indices = range(ndocc) active_indices = range(ndocc, ndocc + nact) # In order to keep the MolecularData class happy, we need a 'valid' molecule molecular_data = MolecularData( geometry=[("H", (0, 0, 0))], basis="", multiplicity=2 ) molecular_data.one_body_integrals = one_body_integrals molecular_data.two_body_integrals = two_body_integrals molecular_data.nuclear_repulsion = nuclear_repulsion_energy hamiltonian = molecular_data.get_molecular_hamiltonian( occupied_indices, active_indices ) return hamiltonian
def get_ham_from_psi4(wfn, mints, n_active_extract=None, freeze_core_extract=False, orbs=None, nuclear_repulsion_energy=0): """Get a molecular Hamiltonian from a Psi4 calculation. Args: wfn (psi4.core.Wavefunction): Psi4 wavefunction object mints (psi4.core.MintsHelper): Psi4 molecular integrals helper n_active_extract (int): number of molecular orbitals to include in the saved Hamiltonian. If None, includes all orbitals, else you must provide active orbitals in orbs. freeze_core_extract (bool): whether to freeze core orbitals as doubly occupied in the saved Hamiltonian. orbs (psi4.core.Matrix): Psi4 orbitals for active space transformations. Must include all occupied (also core in all cases). nuclear_repulsion_energy (float): The ion-ion interaction energy. Returns: hamiltonian (openfermion.ops.InteractionOperator): the electronic Hamiltonian. Note that the ion-ion electrostatic energy is not included. """ assert wfn.same_a_b_orbs(), "Extraction of Hamiltonian from wavefunction" + \ "with different alpha and beta orbitals not yet supported :(" # Note: code refactored to use Psi4 integral-transformation routines # no more storing the whole two-electron integral tensor when only an # active space is needed orbitals = wfn.Ca().to_array(dense=True) one_body_integrals = general_basis_change(np.asarray(mints.ao_kinetic()), orbitals, (1, 0)) one_body_integrals += general_basis_change( np.asarray(mints.ao_potential()), orbitals, (1, 0)) # Build the transformation matrices, i.e. the orbitals for which # we want the integrals, as Psi4.core.Matrix objects n_core_extract = 0 if freeze_core_extract: n_core_extract = wfn.nfrzc() if n_active_extract is None: trf_mat = wfn.Ca() n_active_extract = wfn.nmo() - n_core_extract else: # If orbs is given, it allows us to perform the two-electron integrals # transformation only in the space of active orbitals. Otherwise, we # transform all orbitals and filter them out in the get_ham_from_integrals # function if orbs is None: trf_mat = wfn.Ca() else: assert (orbs.to_array(dense=True).shape[1] == n_active_extract + n_core_extract) trf_mat = orbs two_body_integrals = np.asarray( mints.mo_eri(trf_mat, trf_mat, trf_mat, trf_mat)) n_orbitals = trf_mat.shape[1] two_body_integrals.reshape( (n_orbitals, n_orbitals, n_orbitals, n_orbitals)) two_body_integrals = np.einsum('psqr', two_body_integrals) # Truncate one_body_integrals[np.absolute(one_body_integrals) < EQ_TOLERANCE] = 0. two_body_integrals[np.absolute(two_body_integrals) < EQ_TOLERANCE] = 0. if n_active_extract is None and not freeze_core_extract: occupied_indices = None active_indices = None else: # Indices of occupied molecular orbitals occupied_indices = range(n_core_extract) # Indices of active molecular orbitals active_indices = range(n_core_extract, n_core_extract + n_active_extract) # In order to keep the MolecularData class happy, we need a 'valid' molecule molecular_data = MolecularData(geometry=[('H', (0, 0, 0))], basis='', multiplicity=2) molecular_data.one_body_integrals = one_body_integrals molecular_data.two_body_integrals = two_body_integrals molecular_data.nuclear_repulsion = nuclear_repulsion_energy hamiltonian = molecular_data.get_molecular_hamiltonian( occupied_indices, active_indices) return (hamiltonian)
def vqe_qc(vqe_strategy, vqe_qc_backend, vqe_parametricflag, sample_molecule): """ Initialize a VQE experiment with a custom hamiltonian given as constant input, given a QC-type backend (tomography is always set to True then) """ _vqe = None qc = None vqe_cq = None if vqe_strategy == "custom_program": if vqe_qc_backend == 'Aspen-qvm': qc = get_qc('Aspen-4-2Q-A-qvm') vqe_cq = [1, 2] elif vqe_qc_backend == 'Aspen-pyqvm': qc = get_qc('Aspen-4-2Q-A-pyqvm') vqe_cq = [1, 2] elif vqe_qc_backend == 'Nq-qvm': qc = get_qc('2q-qvm') elif vqe_qc_backend == 'Nq-pyqvm': qc = get_qc('2q-pyqvm') custom_ham = PauliSum([PauliTerm(*x) for x in HAMILTONIAN]) _vqe = VQEexperiment(hamiltonian=custom_ham, qc=qc, custom_qubits=vqe_cq, method='QC', strategy=vqe_strategy, parametric=True, tomography=True, shotN=NSHOTS_SMALL) elif vqe_strategy == "HF": if vqe_qc_backend == 'Aspen-qvm': qc = get_qc('Aspen-4-2Q-A-qvm') vqe_cq = [1, 2] elif vqe_qc_backend == 'Aspen-pyqvm': qc = get_qc('Aspen-4-2Q-A-pyqvm') vqe_cq = [1, 2] elif vqe_qc_backend == 'Nq-qvm': qc = get_qc('2q-qvm') elif vqe_qc_backend == 'Nq-pyqvm': qc = get_qc('2q-pyqvm') cwd = os.path.abspath(os.path.dirname(__file__)) fname = os.path.join(cwd, "resources", "H.hdf5") molecule = MolecularData(filename=fname) _vqe = VQEexperiment(molecule=molecule, qc=qc, custom_qubits=vqe_cq, method='QC', strategy=vqe_strategy, parametric=vqe_parametricflag, tomography=True, shotN=NSHOTS_SMALL) elif vqe_strategy == "UCCSD": if vqe_qc_backend == 'Aspen-qvm': qc = get_qc('Aspen-4-4Q-A-qvm') vqe_cq = [7, 0, 1, 2] elif vqe_qc_backend == 'Aspen-pyqvm': qc = get_qc('Aspen-4-4Q-A-pyqvm') vqe_cq = [7, 0, 1, 2] elif vqe_qc_backend == 'Nq-qvm': qc = get_qc('4q-qvm') elif vqe_qc_backend == 'Nq-pyqvm': qc = get_qc('4q-pyqvm') _vqe = VQEexperiment(molecule=sample_molecule, qc=qc, custom_qubits=vqe_cq, method='QC', strategy=vqe_strategy, parametric=vqe_parametricflag, tomography=True, shotN=NSHOTS_SMALL) return _vqe
def sample_molecule(): cwd = os.path.abspath(os.path.dirname(__file__)) fname = os.path.join(cwd, "resources", "H2.hdf5") molecule = MolecularData(filename=fname) return molecule
def do_make_molecule(self, *args, **kwargs) -> MolecularData: energy = self.compute_energy(method="hf", *args, **kwargs) wfn = self.logs['hf'].wfn molecule = MolecularData(**self.parameters.molecular_data_param) if wfn.nirrep() != 1: wfn = wfn.c1_deep_copy(wfn.basisset()) molecule.one_body_integrals = self.compute_one_body_integrals( ref_wfn=wfn) if "two_body_ordering" not in kwargs: molecule.two_body_integrals = self.compute_two_body_integrals( ref_wfn=wfn) else: molecule.two_body_integrals = self.compute_two_body_integrals( ref_wfn=wfn, ordering=kwargs["two_body_ordering"]) molecule.hf_energy = energy molecule.nuclear_repulsion = wfn.variables( )['NUCLEAR REPULSION ENERGY'] molecule.canonical_orbitals = numpy.asarray(wfn.Ca()) molecule.overlap_integrals = numpy.asarray(wfn.S()) molecule.n_orbitals = molecule.canonical_orbitals.shape[0] molecule.n_qubits = 2 * molecule.n_orbitals molecule.orbital_energies = numpy.asarray(wfn.epsilon_a()) molecule.fock_matrix = numpy.asarray(wfn.Fa()) molecule.save() return molecule
def do_make_molecule(self, molecule=None) -> MolecularData: if molecule is None: molecule = MolecularData(**self.parameters.molecular_data_param) return run_pyscf(molecule, **self.parameters_pyscf.__dict__)
def meanfield( symbols, coordinates, name="molecule", charge=0, mult=1, basis="sto-3g", package="pyscf", outpath=".", ): # pylint: disable=too-many-arguments r"""Generates a file from which the mean field electronic structure of the molecule can be retrieved. This function uses OpenFermion-PySCF and OpenFermion-Psi4 plugins to perform the Hartree-Fock (HF) calculation for the polyatomic system using the quantum chemistry packages ``PySCF`` and ``Psi4``, respectively. The mean field electronic structure is saved in an hdf5-formatted file in the directory ``os.path.join(outpath, package, basis)``. The charge of the molecule can be given to simulate cationic/anionic systems. Also, the spin multiplicity can be input to determine the number of unpaired electrons occupying the HF orbitals as illustrated in the figure below. | .. figure:: ../../_static/qchem/hf_references.png :align: center :width: 50% | Args: symbols (list[str]): symbols of the atomic species in the molecule coordinates (array[float]): 1D array with the atomic positions in Cartesian coordinates. The coordinates must be given in atomic units and the size of the array should be ``3*N`` where ``N`` is the number of atoms. name (str): molecule label charge (int): net charge of the system mult (int): Spin multiplicity :math:`\mathrm{mult}=N_\mathrm{unpaired} + 1` for :math:`N_\mathrm{unpaired}` unpaired electrons occupying the HF orbitals. Possible values for ``mult`` are :math:`1, 2, 3, \ldots`. If not specified, a closed-shell HF state is assumed. basis (str): Atomic basis set used to represent the HF orbitals. Basis set availability per element can be found `here <www.psicode.org/psi4manual/master/basissets_byelement.html#apdx-basiselement>`_ package (str): Quantum chemistry package used to solve the Hartree-Fock equations. Either ``'pyscf'`` or ``'psi4'`` can be used. outpath (str): path to output directory Returns: str: absolute path to the file containing the mean field electronic structure **Example** >>> symbols, coordinates = (['H', 'H'], np.array([0., 0., -0.66140414, 0., 0., 0.66140414])) >>> meanfield(symbols, coordinates, name="h2") ./pyscf/sto-3g/h2 """ if coordinates.size != 3 * len(symbols): raise ValueError( "The size of the array 'coordinates' has to be 3*len(symbols) = {};" " got 'coordinates.size' = {}".format(3 * len(symbols), coordinates.size)) package = package.strip().lower() if package not in ("psi4", "pyscf"): error_message = ( "Integration with quantum chemistry package '{}' is not available. \n Please set" " 'package' to 'pyscf' or 'psi4'.".format(package)) raise TypeError(error_message) package_dir = os.path.join(outpath.strip(), package) basis_dir = os.path.join(package_dir, basis.strip()) if not os.path.isdir(package_dir): os.mkdir(package_dir) os.mkdir(basis_dir) elif not os.path.isdir(basis_dir): os.mkdir(basis_dir) path_to_file = os.path.join(basis_dir, name.strip()) geometry = [[symbol, tuple(coordinates[3 * i:3 * i + 3] * bohr_angs)] for i, symbol in enumerate(symbols)] molecule = MolecularData(geometry, basis, mult, charge, filename=path_to_file) if package == "psi4": run_psi4(molecule, run_scf=1, verbose=0, tolerate_error=1) if package == "pyscf": run_pyscf(molecule, run_scf=1, verbose=0) return path_to_file