# Computing Electron Integrals and Molecular Hamiltonians#

This tutorial is for demonstrating how to obtain the molecular orbital (MO) electron integrals ($$h_{ij}$$ and $$h_{ijkl}$$) as well as the molecular Hamiltonian ($$H$$).

Here, we adopt the physicists’ convention for the electron integrals, so that they are related to the Hamiltonian by the equation:

$$H = E_{\text{nuc}} + \sum_{i, j = 1}^{N} h_{ij} c_i^{\dagger} c_j + \frac{1}{2} \sum_{i, j, k, l = 1}^{N} h_{ijkl} c_i^{\dagger} c_j^{\dagger} c_k c_l, \nonumber$$

where

• $$E_{\text{nuc}}$$ is the nuclear repulsion energy.

• $$h_{ij}$$ is the 1-electron MO integral (physicist’s convention).

• $$h_{ijkl}$$ is the 2-electron MO integral (physicist’s convention).

• $$c_i^{\dagger}$$, $$c_i$$ are the fermionic creation and annihilation operators on the i-th spin orbtial.

• $$N$$ is the number of spin oribtals.

## Prerequisite#

QURI Parts modules used in this tutorial: quri-parts-chemquri-parts-pyscf, and quri-parts-openfermion. You can install them as follows:

[1]:

!pip install "quri_parts[chem]"
!pip install "quri_parts[pyscf]"
!pip install "quri_parts[openfermion]"


## Quick Overview#

First, let’s have a quick overview of the steps necessary for constructing the molecular Hamiltonian for a given molecule.

### Step 1: Define the molecule#

[2]:

from pyscf import gto, scf

h2o_atom_list = [['H', [0, 0, 0]], ['O', [2, 0, 1]], ['H',  [0, 0, 2]]]
h2o_mol = gto.M(atom=h2o_atom_list, verbose=0)
h2o_mf = scf.RHF(h2o_mol).run()
h2o_mo_coeff = h2o_mf.mo_coeff  # The mo coefficient of the H2O molecule.


### Step 2: Compute the MO elctron integrals#

[3]:

from quri_parts.pyscf.mol import get_spin_mo_integrals_from_mole
from quri_parts.chem.mol import ActiveSpace

full_space, mo_eint_set = get_spin_mo_integrals_from_mole(h2o_mol, h2o_mo_coeff)
active_space, active_space_mo_eint_set = get_spin_mo_integrals_from_mole(
h2o_mol,
h2o_mo_coeff,
ActiveSpace(6, 4)
)


### Step 3: Obtain the Qubit Hamiltonian#

[4]:

from quri_parts.openfermion.mol import get_qubit_mapped_hamiltonian

# Full space qubit hamiltonian
full_space_jw_hamiltonian, mapping = get_qubit_mapped_hamiltonian(
full_space, mo_eint_set
)

# Active space qubit hamiltonian
active_space_jw_hamiltonian, mapping = get_qubit_mapped_hamiltonian(
active_space, active_space_mo_eint_set,
)


## Defining the Molecule#

First, let’s create the molecule we are interested in. In later part of this tutorial, we will be using quri-parts-pyscf to perform the computation for electron integrals. So, we create the molecule using the pyscf library.

[5]:

from pyscf import gto

h2o_atom_list = [['H', [0, 0, 0]], ['O', [2, 0, 1]], ['H',  [0, 0, 2]]]
h2o_mol = gto.M(atom=h2o_atom_list, verbose = 0)


Another key component of computing the MO electron integral is the MO coefficients, which can also be computed using the pyscf library.

[6]:

from pyscf import scf

h2o_mf = scf.RHF(h2o_mol).run()
h2o_mo_coeff = h2o_mf.mo_coeff  # The MO coefficient of the H2O molecule.


## Computing the MO electron integrals#

Having prepared the molecule and the corresponding electron integrals, we may now compute the MO electron integrals. In QURI Parts, the molecular orbital electron integrals (MO eInts) are represented by a SpinMOeIntSet object.

[7]:

from quri_parts.pyscf.mol import get_spin_mo_integrals_from_mole

full_space, mo_eint_set = get_spin_mo_integrals_from_mole(h2o_mol, h2o_mo_coeff)


The mo_eint_set variable we created above is a SpinMOeIntSet that contains the nuclear repulsion energy $$E_{\text{nuc}}$$ and the electron integrals $$h_{ij}$$ and $$h_{ijkl}$$. We may access them with:

[8]:

nuclear_energy = mo_eint_set.const
mo_1e_int = mo_eint_set.mo_1e_int.array
mo_2e_int = mo_eint_set.mo_2e_int.array


The full_space variable is an ActiveSpace object that contains the number of active spatial orbitals and active electrons involved in the system, which we introduce briefly in the next section.

[9]:

n_spatial_orbitals = full_space.n_active_orb
n_spatial_electrons = full_space.n_active_ele


### Active Space and the active space MO electron integrals#

In quri-parts, the active space is represented by the ActiveSpace object.

[10]:

from quri_parts.chem.mol import ActiveSpace

active_space = ActiveSpace(n_active_ele=6, n_active_orb=4)


To obtain the active space MO electron integrals, we pass in the ActiveSpace object we just created into the get_spin_mo_integrals_from_mole function.

[11]:

active_space, active_space_mo_eint_set = get_spin_mo_integrals_from_mole(h2o_mol, h2o_mo_coeff, active_space)

active_space_core_energy = active_space_mo_eint_set.const
active_space_1e_int = active_space_mo_eint_set.mo_1e_int.array
active_space_2e_int = active_space_mo_eint_set.mo_2e_int.array


## Computing the molecular Hamiltonian#

After obtaining the MO electron integrals, we may start to construct the molecular Hamiltonian. We introduce the procedures of computing the fermionic Hamiltonian as well as the qubit Hamiltonian.

### Obtaining the fermionic Hamiltonian and converting it to the qubit Hamiltonian#

The fermionic Hamiltonian can be directly constructed using the mo_eint_set or active_space_mo_eint_set we obtained before

[12]:

from quri_parts.openfermion.mol import get_fermionic_hamiltonian

full_space_fermionic_hamiltonian = get_fermionic_hamiltonian(mo_eint_set)
active_space_fermionic_hamiltonian = get_fermionic_hamiltonian(active_space_mo_eint_set)


To perform any further computation with QURI Parts, e.g. estimate Hamiltonian expectation value for a quantum state, we need to perform fermion-qubit mapping to the fermionic hamiltonian we just obtained. We also provide the operator_from_of_fermionic_op function for this purpose.

[13]:

from quri_parts.openfermion.mol import operator_from_of_fermionic_op
from quri_parts.openfermion.transforms import jordan_wigner

# Full space qubit hamiltonian

full_space_jw_hamiltonian, full_space_mapping = operator_from_of_fermionic_op(
full_space_fermionic_hamiltonian,
full_space,
sz=None,                # Default to None
fermion_qubit_mapping=jordan_wigner  # Default to jordan wigner.
)

# Active space qubit hamiltonian

active_space_jw_hamiltonian, active_space_mapping = operator_from_of_fermionic_op(
active_space_fermionic_hamiltonian,
active_space,
sz=None,                # Default to None
fermion_qubit_mapping=jordan_wigner  # Default to jordan wigner.
)


The full_space_jw_hamiltonian and active_space_jw_hamiltonian are the hamiltonian we desired. The full_space_mapping and active_space_mapping are objects that are able to perform fermion-qubit mapping for other operators and states in further computations. Their usage can be found in the Fermion-Qubit Mapping Hamiltonian Tutorial.

### Shortcut for obtaining the qubit Hamiltonian#

After obtaining the active space and the MO electron integrals, we may obtain the qubit Hamiltonian directly without going through the fermionic Hamiltonian. This can be done by the function

[14]:

from quri_parts.openfermion.mol import get_qubit_mapped_hamiltonian

# Full space qubit hamiltonian
full_space_jw_hamiltonian, full_space_mapping = get_qubit_mapped_hamiltonian(
full_space,
mo_eint_set,
sz=None,                # Default to None
fermion_qubit_mapping=jordan_wigner  # Default to jordan wigner.
)

# Active space qubit hamiltonian
active_space_jw_hamiltonian, active_space_mapping = get_qubit_mapped_hamiltonian(
active_space,
active_space_mo_eint_set,
sz=None,                # Default to None
fermion_qubit_mapping=jordan_wigner  # Default to jordan wigner.
)