Пример #1
0
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)
Пример #2
0
    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)
    MolecularMeta['FCI eigenstates'] = run_FCI(molecule, vqd_maxiter + 8)

    # Generate operator pool
    if not isinstance(SymbolicAnsatz, Circuit):
        pool.init(n_orb=molecule.n_orbitals,
                  n_occ=molecule.get_n_alpha_electrons(),
                  n_vir=molecule.n_orbitals - molecule.get_n_alpha_electrons())

    opt_eigen_ops = [
    ]  # list of sublists (index of operators used in each eigenstate ansatz)
    opt_eigen_params = [
Пример #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
Пример #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)