Пример #1
0
def make_hn(
    hcount, distance
) -> Tuple[RestrictedHartreeFockObjective, of.MolecularData, np.ndarray,
           np.ndarray, np.ndarray]:

    shcount = str(hcount)
    sdistance = str(distance)
    cwd = os.path.dirname(os.path.realpath(__file__))
    hn_path = str(
        Path(cwd + "/molecular_data/hydrogen_chains/h_" + shcount +
             "_sto-3g/bond_distance_" + sdistance + "/"))
    hdf5name = str("H" + shcount + "_sto-3g_singlet_linear_r-" + sdistance +
                   ".hdf5")
    molfile = os.path.join(hn_path, hdf5name)

    molecule = of.MolecularData(filename=molfile)
    molecule.load()

    S = np.load(os.path.join(hn_path, 'overlap.npy'))
    Hcore = np.load(os.path.join(hn_path, 'h_core.npy'))
    TEI = np.load(os.path.join(hn_path, 'tei.npy'))

    _, X = sp.linalg.eigh(Hcore, S)
    obi = of.general_basis_change(Hcore, X, (1, 0))
    tbi = np.einsum('psqr', of.general_basis_change(TEI, X, (1, 0, 1, 0)))
    molecular_hamiltonian = generate_hamiltonian(obi, tbi,
                                                 molecule.nuclear_repulsion)

    rhf_objective = RestrictedHartreeFockObjective(molecular_hamiltonian,
                                                   molecule.n_electrons)

    scipy_result = rhf_minimization(rhf_objective)

    return rhf_objective, molecule, scipy_result.x, obi, tbi
def make_h3_2_5() -> Tuple[RestrictedHartreeFockObjective, of.MolecularData,
                           np.ndarray, np.ndarray, np.ndarray]:
    # load the molecule from moelcular data
    h3_2_5_path = os.path.join(
        hfvqe.__path__[0],
        'molecular_data/hydrogen_chains/h_3_p_sto-3g/bond_distance_2.5')

    molfile = os.path.join(h3_2_5_path,
                           'H3_plus_sto-3g_singlet_linear_r-2.5.hdf5')
    molecule = of.MolecularData(filename=molfile)
    molecule.load()

    S = np.load(os.path.join(h3_2_5_path, 'overlap.npy'))
    Hcore = np.load(os.path.join(h3_2_5_path, 'h_core.npy'))
    TEI = np.load(os.path.join(h3_2_5_path, 'tei.npy'))

    _, X = sp.linalg.eigh(Hcore, S)
    obi = of.general_basis_change(Hcore, X, (1, 0))
    tbi = np.einsum('psqr', of.general_basis_change(TEI, X, (1, 0, 1, 0)))
    molecular_hamiltonian = generate_hamiltonian(obi, tbi,
                                                 molecule.nuclear_repulsion)

    rhf_objective = RestrictedHartreeFockObjective(molecular_hamiltonian,
                                                   molecule.n_electrons)

    scipy_result = rhf_minimization(rhf_objective)
    return rhf_objective, molecule, scipy_result.x, obi, tbi
Пример #3
0
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)
Пример #4
0
def make_h3_2_5(molecular_data_directory=None) \
        -> Tuple[RestrictedHartreeFockObjective, of.MolecularData,
                 np.ndarray, np.ndarray, np.ndarray]:
    if molecular_data_directory is None:
        molecular_data_directory = _MOLECULAR_DATA_DIRECTORY

    h3_2_5_path = f'{molecular_data_directory}/hydrogen_chains/h_3_p_sto-3g/bond_distance_2.5'
    molfile = f'{h3_2_5_path}/H3_plus_sto-3g_singlet_linear_r-2.5.hdf5'
    molecule = of.MolecularData(filename=molfile)
    molecule.load()

    S = np.load(os.path.join(h3_2_5_path, 'overlap.npy'))
    Hcore = np.load(os.path.join(h3_2_5_path, 'h_core.npy'))
    TEI = np.load(os.path.join(h3_2_5_path, 'tei.npy'))

    _, X = sp.linalg.eigh(Hcore, S)
    obi = of.general_basis_change(Hcore, X, (1, 0))
    tbi = np.einsum('psqr', of.general_basis_change(TEI, X, (1, 0, 1, 0)))
    molecular_hamiltonian = generate_hamiltonian(obi, tbi,
                                                 molecule.nuclear_repulsion)

    rhf_objective = RestrictedHartreeFockObjective(molecular_hamiltonian,
                                                   molecule.n_electrons)

    scipy_result = rhf_minimization(rhf_objective)
    return rhf_objective, molecule, scipy_result.x, obi, tbi
Пример #5
0
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 make_rhf_objective(molecule: of.MolecularData):
    S, Hcore, TEI = get_ao_integrals(molecule)
    _, X = sp.linalg.eigh(Hcore, S)

    obi = of.general_basis_change(Hcore, X, (1, 0))
    tbi = np.einsum('psqr', of.general_basis_change(TEI, X, (1, 0, 1, 0)))
    molecular_hamiltonian = generate_hamiltonian(obi, tbi,
                                                 molecule.nuclear_repulsion)

    rhf_objective = RestrictedHartreeFockObjective(molecular_hamiltonian,
                                                   molecule.n_electrons)
    return rhf_objective, S, Hcore, TEI, obi, tbi
Пример #7
0
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)
Пример #8
0
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
Пример #9
0
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)