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-community.github.io/qiskit-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 0x7f0ea17f8970>
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 in physicist order!
[3]:
hamiltonian = problem.hamiltonian
coefficients = hamiltonian.electronic_integrals
print(coefficients.alpha)
Polynomial Tensor
"+-":
array([[-1.25633907e+00, -1.37083854e-17],
[-6.07732712e-17, -4.71896007e-01]])
"++--":
array([6.75710155e-01, 1.38777878e-16, 1.80931200e-01, 6.64581730e-01,
5.55111512e-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
0.33785507740175813 * ( +_0 +_0 -_0 -_0 )
+ 0.3322908651276482 * ( +_0 +_1 -_1 -_0 )
+ 0.33785507740175813 * ( +_0 +_2 -_2 -_0 )
+ 0.3322908651276482 * ( +_0 +_3 -_3 -_0 )
+ 0.09046559989211572 * ( +_0 +_0 -_1 -_1 )
+ 0.09046559989211572 * ( +_0 +_1 -_0 -_1 )
+ 0.09046559989211572 * ( +_0 +_2 -_3 -_1 )
+ 0.09046559989211572 * ( +_0 +_3 -_2 -_1 )
+ 0.09046559989211572 * ( +_1 +_0 -_1 -_0 )
+ 0.09046559989211572 * ( +_1 +_1 -_0 -_0 )
+ 0.09046559989211572 * ( +_1 +_2 -_3 -_0 )
+ 0.09046559989211572 * ( +_1 +_3 -_2 -_0 )
+ 0.3322908651276482 * ( +_1 +_0 -_0 -_1 )
+ 0.34928686136600884 * ( +_1 +_1 -_1 -_1 )
+ 0.3322908651276482 * ( +_1 +_2 -_2 -_1 )
+ 0.34928686136600884 * ( +_1 +_3 -_3 -_1 )
+ 0.33785507740175813 * ( +_2 +_0 -_0 -_2 )
+ 0.3322908651276482 * ( +_2 +_1 -_1 -_2 )
+ 0.33785507740175813 * ( +_2 +_2 -_2 -_2 )
+ 0.3322908651276482 * ( +_2 +_3 -_3 -_2 )
+ 0.09046559989211572 * ( +_2 +_0 -_1 -_3 )
+ 0.09046559989211572 * ( +_2 +_1 -_0 -_3 )
+ 0.09046559989211572 * ( +_2 +_2 -_3 -_3 )
+ 0.09046559989211572 * ( +_2 +_3 -_2 -_3 )
+ 0.09046559989211572 * ( +_3 +_0 -_1 -_2 )
+ 0.09046559989211572 * ( +_3 +_1 -_0 -_2 )
+ 0.09046559989211572 * ( +_3 +_2 -_3 -_2 )
+ 0.09046559989211572 * ( +_3 +_3 -_2 -_2 )
+ 0.3322908651276482 * ( +_3 +_0 -_0 -_3 )
+ 0.34928686136600884 * ( +_3 +_1 -_1 -_3 )
+ 0.3322908651276482 * ( +_3 +_2 -_2 -_3 )
+ 0.34928686136600884 * ( +_3 +_3 -_3 -_3 )
+ -1.25633907300325 * ( +_0 -_0 )
+ -0.47189600728114184 * ( +_1 -_1 )
+ -1.25633907300325 * ( +_2 -_2 )
+ -0.47189600728114184 * ( +_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 0x7f0ea17f85e0>
[12]:
problem.properties.particle_number
[12]:
<qiskit_nature.second_q.properties.particle_number.ParticleNumber at 0x7f0ea17f8c40>
[13]:
problem.properties.angular_momentum
[13]:
<qiskit_nature.second_q.properties.angular_momentum.AngularMomentum at 0x7f0ea17f8490>
[14]:
problem.properties.magnetization
[14]:
<qiskit_nature.second_q.properties.magnetization.Magnetization at 0x7f0ea17f88b0>
[15]:
problem.properties.electronic_dipole_moment
[15]:
<qiskit_nature.second_q.properties.dipole_moment.ElectronicDipoleMoment at 0x7f0ea17f8f10>
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 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
[18]:
import tutorial_magics
%qiskit_version_table
%qiskit_copyright
Version Information
Software | Version |
---|---|
qiskit | 1.0.1 |
qiskit_algorithms | 0.3.0 |
qiskit_nature | 0.7.2 |
System information | |
Python version | 3.8.18 |
OS | Linux |
Fri Feb 23 10:25:01 2024 UTC |
This code is a part of a Qiskit project
© Copyright IBM 2017, 2024.
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.