Note

This page was generated from docs/tutorials/01_electronic_structure.ipynb.

# Electronic structure¶

## Introduction¶

The molecular Hamiltonian is

Because the nuclei are much heavier than the electrons they do not move on the same time scale and therefore, the behavior of nuclei and electrons can be decoupled. This is the Born-Oppenheimer approximation.

Therefore, one can first tackle the electronic problem with the nuclear coordinates entering only as parameters. The energy levels of the electrons in the molecule can then be found by solving the non-relativistic time independent Schrödinger equation,

where

In particular the ground state energy is given by:

where \(\Psi_0\) is the ground state of the system.

However, the dimensionality of this problem grows exponentially with the number of degrees of freedom. To tackle this issue we would like to prepare \(\Psi_0\) on a quantum computer and measure the Hamiltonian expectation value (or \(E_0\)) directly.

So how do we do that concretely?

## Starting from the Hartree-Fock solution¶

A good starting point for solving this problem is the Hartree-Fock (HF) method. This method approximates the N-body problem by N one-body problems where each electron evolves in the mean-field of the others. Classically solving the HF equations is efficient and leads to the exact exchange energy but does not include any electron correlation. Therefore, it is usually a good starting point to which to add correlation.

The Hamiltonian can then be re-expressed in the basis of the solutions of the HF method, also called Molecular Orbitals (MOs):

with the 1-body integrals

and 2-body integrals

The MOs (\(\phi_u\)) can be occupied or virtual (unoccupied). One MO can contain 2 electrons. However, in what follows we actually work with Spin Orbitals which are associated with a spin up (\(\alpha\)) of spin down (\(\beta\)) electron. Thus Spin Orbitals can contain one electron or be unoccupied.

Note: when referring to the number of orbitals, we will be using the number of *spatial* orbitals. This refers to any orbital in Cartesian space (whether its a molecular orbital or in another basis does not matter here). Each spatial orbital is then generally split into two *spin* orbitals.

We now show how to concretely realise these steps with Qiskit.

### Obtaining an initial Hartree-Fock solution¶

Qiskit is interfaced with different classical codes which are able to find the HF solutions. Interfacing between Qiskit and the following codes is already available:

Gaussian

Psi4

PySCF

In the following we set up a PySCF driver, for the hydrogen molecule at equilibrium bond length (0.735 angstrom) in the singlet state and with no charge.

```
[1]:
```

```
from qiskit_nature.units import DistanceUnit
from qiskit_nature.second_q.drivers import PySCFDriver
driver = PySCFDriver(
atom="H 0 0 0; H 0 0 0.735",
basis="sto3g",
charge=0,
spin=0,
unit=DistanceUnit.ANGSTROM,
)
```

Running this driver, will yield an `ElectronicStructureProblem`

, Qiskit Nature’s representation of the electronic structure problem which we are interested in solving. For further information about the drivers see https://qiskit.org/documentation/nature/apidocs/qiskit_nature.second_q.drivers.html

```
[2]:
```

```
problem = driver.run()
print(problem)
```

```
<qiskit_nature.second_q.problems.electronic_structure_problem.ElectronicStructureProblem object at 0x7f4274250af0>
```

```
/opt/hostedtoolcache/Python/3.8.17/x64/lib/python3.8/site-packages/pyscf/dft/libxc.py:772: UserWarning: Since PySCF-2.3, B3LYP (and B3P86) are changed to the VWN-RPA variant, the same to the B3LYP functional in Gaussian and ORCA (issue 1480). To restore the VWN5 definition, you can put the setting "B3LYP_WITH_VWN5 = True" in pyscf_conf.py
warnings.warn('Since PySCF-2.3, B3LYP (and B3P86) are changed to the VWN-RPA variant, '
```

### The `ElectronicStructureProblem`

and its components¶

Let us spend some time to understand this problem instance and its components.

#### The `ElectronicEnergy`

Hamiltonian¶

The most important aspect is the internal Hamiltonian; in this case an `ElectronicEnergy`

hamiltonian. This class is able to generate the second-quantized operator from the 1- and 2-body integrals which the classical code has computed for us.

IMPORTANT:The container class for the integral coefficients (`PolynomialTensor`

) requires the 2-body terms to be provided inphysicist order!

```
[3]:
```

```
hamiltonian = problem.hamiltonian
coefficients = hamiltonian.electronic_integrals
print(coefficients.alpha)
```

```
Polynomial Tensor
"+-":
[[-1.25633907e+00 -1.37083854e-17]
[-6.07732712e-17 -4.71896007e-01]]
"++--":
[[[[6.75710155e-01 1.69253442e-16]
[1.56722377e-16 1.80931200e-01]]
[[4.84650299e-17 1.80931200e-01]
[6.64581730e-01 3.79897400e-16]]]
[[[1.01440795e-16 6.64581730e-01]
[1.80931200e-01 4.71502663e-17]]
[[1.80931200e-01 3.78920172e-16]
[6.59828421e-17 6.98573723e-01]]]]
```

```
[4]:
```

```
second_q_op = hamiltonian.second_q_op()
print(second_q_op)
```

```
Fermionic Operator
number spin orbitals=4, number terms=36
-1.25633907300325 * ( +_0 -_0 )
+ -0.47189600728114184 * ( +_1 -_1 )
+ -1.25633907300325 * ( +_2 -_2 )
+ -0.47189600728114184 * ( +_3 -_3 )
+ 0.33785507740175813 * ( +_0 +_0 -_0 -_0 )
+ 0.09046559989211568 * ( +_0 +_0 -_1 -_1 )
+ 0.09046559989211564 * ( +_0 +_1 -_0 -_1 )
+ 0.33229086512764827 * ( +_0 +_1 -_1 -_0 )
+ 0.33785507740175813 * ( +_0 +_2 -_2 -_0 )
+ 0.09046559989211568 * ( +_0 +_2 -_3 -_1 )
+ 0.09046559989211564 * ( +_0 +_3 -_2 -_1 )
+ 0.33229086512764827 * ( +_0 +_3 -_3 -_0 )
+ 0.3322908651276482 * ( +_1 +_0 -_0 -_1 )
+ 0.09046559989211574 * ( +_1 +_0 -_1 -_0 )
+ 0.09046559989211565 * ( +_1 +_1 -_0 -_0 )
+ 0.3492868613660089 * ( +_1 +_1 -_1 -_1 )
+ 0.3322908651276482 * ( +_1 +_2 -_2 -_1 )
+ 0.09046559989211574 * ( +_1 +_2 -_3 -_0 )
+ 0.09046559989211565 * ( +_1 +_3 -_2 -_0 )
+ 0.3492868613660089 * ( +_1 +_3 -_3 -_1 )
+ 0.33785507740175813 * ( +_2 +_0 -_0 -_2 )
+ 0.09046559989211568 * ( +_2 +_0 -_1 -_3 )
+ 0.09046559989211564 * ( +_2 +_1 -_0 -_3 )
+ 0.33229086512764827 * ( +_2 +_1 -_1 -_2 )
+ 0.33785507740175813 * ( +_2 +_2 -_2 -_2 )
+ 0.09046559989211568 * ( +_2 +_2 -_3 -_3 )
+ 0.09046559989211564 * ( +_2 +_3 -_2 -_3 )
+ 0.33229086512764827 * ( +_2 +_3 -_3 -_2 )
+ 0.3322908651276482 * ( +_3 +_0 -_0 -_3 )
+ 0.09046559989211574 * ( +_3 +_0 -_1 -_2 )
+ 0.09046559989211565 * ( +_3 +_1 -_0 -_2 )
+ 0.3492868613660089 * ( +_3 +_1 -_1 -_3 )
+ 0.3322908651276482 * ( +_3 +_2 -_2 -_3 )
+ 0.09046559989211574 * ( +_3 +_2 -_3 -_2 )
+ 0.09046559989211565 * ( +_3 +_3 -_2 -_2 )
+ 0.3492868613660089 * ( +_3 +_3 -_3 -_3 )
```

Note, that this is purely the **electronic** Hamiltonian of the system. That means, that the *nuclear repulsion energy* is not included. Instead, Qiskit Nature will add this constant energy offset in a post-processing step, in order to compute the total energy of your system. To learn how to include the nuclear repulsion energy in this operator, please refer to the documentation of the `ElectronicEnergy`

class
here.

```
[5]:
```

```
hamiltonian.nuclear_repulsion_energy # NOT included in the second_q_op above
```

```
[5]:
```

```
0.7199689944489797
```

#### More attributes of the `ElectronicStructureProblem`

¶

Below we list some additional attributes of our `problem`

instance:

```
[6]:
```

```
problem.molecule
```

```
[6]:
```

```
MoleculeInfo(symbols=['H', 'H'], coords=[(0.0, 0.0, 0.0), (0.0, 0.0, 1.3889487015553204)], multiplicity=1, charge=0, units=<DistanceUnit.BOHR: 'Bohr'>, masses=[1, 1])
```

```
[7]:
```

```
problem.reference_energy
```

```
[7]:
```

```
-1.116998996754004
```

```
[8]:
```

```
problem.num_particles
```

```
[8]:
```

```
(1, 1)
```

```
[9]:
```

```
problem.num_spatial_orbitals
```

```
[9]:
```

```
2
```

```
[10]:
```

```
problem.basis
```

```
[10]:
```

```
<ElectronicBasis.MO: 'molecular'>
```

To learn more about the basis of your problem, please refer to the tutorial on the ``BasisTransformer`

<05_problem_transformers.ipynb>`__.

#### Additional observables¶

The `ElectronicStructureProblem`

also contains additional operator factories, which will generate observables to be evaluated at the ground- and excited-states at the end of your computation.

```
[11]:
```

```
problem.properties
```

```
[11]:
```

```
<qiskit_nature.second_q.problems.electronic_properties_container.ElectronicPropertiesContainer at 0x7f4274250be0>
```

```
[12]:
```

```
problem.properties.particle_number
```

```
[12]:
```

```
<qiskit_nature.second_q.properties.particle_number.ParticleNumber at 0x7f4274250c70>
```

```
[13]:
```

```
problem.properties.angular_momentum
```

```
[13]:
```

```
<qiskit_nature.second_q.properties.angular_momentum.AngularMomentum at 0x7f4274250b50>
```

```
[14]:
```

```
problem.properties.magnetization
```

```
[14]:
```

```
<qiskit_nature.second_q.properties.magnetization.Magnetization at 0x7f4274250bb0>
```

```
[15]:
```

```
problem.properties.electronic_dipole_moment
```

```
[15]:
```

```
<qiskit_nature.second_q.properties.dipole_moment.ElectronicDipoleMoment at 0x7f4274250cd0>
```

For more information about these properties, please refer to their tutorial.

## Solving the `ElectronicStructureProblem`

¶

In the following, we will compute the ground-state of our problem instance. To learn more about the individual components that go into the `GroundStateSolver`

, please refer to the ground state tutorial.

```
[16]:
```

```
from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver
from qiskit_nature.second_q.algorithms import GroundStateEigensolver
from qiskit_nature.second_q.mappers import JordanWignerMapper
solver = GroundStateEigensolver(
JordanWignerMapper(),
NumPyMinimumEigensolver(),
)
```

```
[17]:
```

```
result = solver.solve(problem)
print(result)
```

```
=== GROUND STATE ENERGY ===
* Electronic ground state energy (Hartree): -1.857275030202
- computed part: -1.857275030202
~ Nuclear repulsion energy (Hartree): 0.719968994449
> Total ground state energy (Hartree): -1.137306035753
=== MEASURED OBSERVABLES ===
0: # Particles: 2.000 S: 0.000 S^2: 0.000 M: 0.000
=== DIPOLE MOMENTS ===
~ Nuclear dipole moment (a.u.): [0.0 0.0 1.3889487]
0:
* Electronic dipole moment (a.u.): [0.0 0.0 1.388948701555]
- computed part: [0.0 0.0 1.388948701555]
> Dipole moment (a.u.): [0.0 0.0 -0.000000001555] Total: 0.000000001555
(debye): [0.0 0.0 -0.000000003953] Total: 0.000000003953
```

```
/home/runner/work/qiskit-nature/qiskit-nature/qiskit_nature/deprecation.py:297: PauliSumOpDeprecationWarning: PauliSumOp is deprecated as of version 0.6.0 and support for them will be removed no sooner than 3 months after the release. Instead, use SparsePauliOp. You can switch to SparsePauliOp immediately, by setting `qiskit_nature.settings.use_pauli_sum_op` to `False`.
return func(*args, **kwargs)
```

```
[18]:
```

```
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
```

### Version Information

Software | Version |
---|---|

`qiskit` | None |

`qiskit-terra` | 0.25.1 |

`qiskit_nature` | 0.6.2 |

System information | |

Python version | 3.8.17 |

Python compiler | GCC 11.3.0 |

Python build | default, Jun 7 2023 12:29:56 |

OS | Linux |

CPUs | 2 |

Memory (Gb) | 6.7694854736328125 |

Fri Sep 01 17:56:17 2023 UTC |

### This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may

obtain a copy of this license in the LICENSE.txt file in the root directory

of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this

copyright notice, and modified files need to carry a notice indicating

that they have been altered from the originals.