Esempio n. 1
0
    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 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))
Esempio n. 3
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
Esempio n. 4
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)