NaH dissociation curve using VQE with UCCSD

This notebook demonstrates using the Qiskit Aqua Chemistry to plot graphs of the ground state energy of the Sodium Hydride (NaH) molecule over a range of inter-atomic distances using VQE and UCCSD. It is compared to the same energies as computed by the ExactEigensolver

This notebook populates a dictionary, that is a progammatic representation of an input file, in order to drive the Qiskit Aqua Chemistry stack. Such a dictionary can be manipulated programmatically and this is indeed the case here where we alter the molecule supplied to the driver in each loop.

This notebook has been written to use the PYSCF chemistry driver. See the PYSCF chemistry driver readme if you need to install the external PySCF library that this driver requires.


In [1]:
import numpy as np
import pylab
from qiskit_aqua_chemistry import AquaChemistry

# Input dictionary to configure Qiskit Aqua Chemistry for the chemistry problem.
aqua_chemistry_dict = {
    'driver': {'name': 'PYSCF'},
    'PYSCF': {'atom': '', 'basis': 'sto3g'},
    'operator': {'name': 'hamiltonian', 'qubit_mapping': 'parity',
                 'two_qubit_reduction': True, 'freeze_core': True, 'orbital_reduction': []},
    'algorithm': {'name': ''},
    'optimizer': {'name': 'COBYLA', 'maxiter': 10000 },
    'variational_form': {'name': 'UCCSD'},
    'initial_state': {'name': 'HartreeFock'}
}
molecule = 'H .0 .0 -{0}; Na .0 .0 {0}'
algorithms = ['VQE', 'ExactEigensolver']

pts  = [x * 0.1  for x in range(10, 25)]
pts += [x * 0.25 for x in range(10, 18)]
pts += [4.5]
energies = np.empty([len(algorithms), len(pts)])
hf_energies = np.empty(len(pts))
distances = np.empty(len(pts))
dipoles     = np.empty([len(algorithms), len(pts)])
eval_counts = np.empty(len(pts))

print('Processing step __', end='')
for i, d in enumerate(pts):
    print('\b\b{:2d}'.format(i), end='', flush=True)
    aqua_chemistry_dict['PYSCF']['atom'] = molecule.format(d/2) 
    for j in range(len(algorithms)):
        aqua_chemistry_dict['algorithm']['name'] = algorithms[j] 
        solver = AquaChemistry()
        result = solver.run(aqua_chemistry_dict)
        energies[j][i] = result['energy']
        hf_energies[i] = result['hf_energy']
        dipoles[j][i]  = result['total_dipole_moment'] / 0.393430307
        if algorithms[j] == 'VQE':
            eval_counts[i] = result['algorithm_retvals']['eval_count']
    distances[i] = d
print(' --- complete')

print('Distances: ', distances)
print('Energies:', energies)
print('Hartree-Fock energies:', hf_energies)
print('Dipoles:', dipoles)
print('VQE num evaluations:', eval_counts)


Processing step 23 --- complete
Distances:  [1.   1.1  1.2  1.3  1.4  1.5  1.6  1.7  1.8  1.9  2.   2.1  2.2  2.3
 2.4  2.5  2.75 3.   3.25 3.5  3.75 4.   4.25 4.5 ]
Energies: [[-160.0584908  -160.15699853 -160.22568738 -160.27202157 -160.30172257
  -160.31895195 -160.32675454 -160.32741543 -160.32269884 -160.31400293
  -160.30245857 -160.2889906  -160.27435549 -160.25916613 -160.24391108
  -160.22897215 -160.19475712 -160.16708738 -160.14746324 -160.13627081
  -160.1312006  -160.12966451 -160.12935288 -160.1272386 ]
 [-160.05849084 -160.15699856 -160.22568741 -160.2720216  -160.30172261
  -160.31895199 -160.32675458 -160.32741545 -160.32269886 -160.31400297
  -160.30245861 -160.28899063 -160.27435552 -160.25916618 -160.24391112
  -160.22897222 -160.19475719 -160.16708762 -160.14746354 -160.13627173
  -160.13150727 -160.12988489 -160.12941537 -160.12738873]]
Hartree-Fock energies: [-160.04320295 -160.14360744 -160.21336733 -160.26022033 -160.29007462
 -160.30721237 -160.31476208 -160.31507193 -160.30995602 -160.30085169
 -160.28891892 -160.2751014  -160.26016389 -160.24471683 -160.2292359
 -160.21408033 -160.17913095 -160.14978812 -160.12634274 -160.10810649
 -160.09400858 -160.08298959 -160.07419396 -160.0607817 ]
Dipoles: [[2.97283503 3.47766098 3.89571273 4.26007211 4.59366828 4.91064169
  5.21881014 5.52062327 5.82225205 6.12073518 6.41351277 6.70026841
  6.97550548 7.22874789 7.45326529 7.64302302 7.80687793 7.21426635
  5.34909309 2.7107585  1.0689969  0.21149191 0.05667558 0.03530844]
 [2.97335246 3.47789485 3.89561999 4.26006188 4.59374084 4.91025573
  5.21772576 5.52078168 5.82151088 6.11992744 6.41423476 6.70095324
  6.97491033 7.22906568 7.45413201 7.63797444 7.80073442 7.19343854
  5.31627389 2.65735429 0.91782197 0.26885135 0.07470177 0.0219034 ]]
VQE num evaluations: [  542.   570.   598.   579.   511.   546.   545.   519.   544.   555.
   562.   610.   591.   642.   695.   758.   982.  1400.  2393.  5254.
 10000. 10000.  3549. 10000.]

In [2]:
pylab.plot(distances, hf_energies, label='Hartree-Fock')
for j in range(len(algorithms)):
    pylab.plot(distances, energies[j], label=algorithms[j])
pylab.xlabel('Interatomic distance')
pylab.ylabel('Energy')
pylab.title('NaH Ground State Energy')
pylab.legend(loc='upper right')


Out[2]:
<matplotlib.legend.Legend at 0x10ec48dd8>

In [3]:
pylab.plot(distances, np.subtract(hf_energies, energies[1]), label='Hartree-Fock')
pylab.plot(distances, np.subtract(energies[0], energies[1]), label='VQE')
pylab.xlabel('Interatomic distance')
pylab.ylabel('Energy')
pylab.title('Energy difference from ExactEigensolver')
pylab.legend(loc='upper left')


Out[3]:
<matplotlib.legend.Legend at 0x10e626908>

In [4]:
for j in reversed(range(len(algorithms))):
    pylab.plot(distances, dipoles[j], label=algorithms[j])
pylab.xlabel('Interatomic distance')
pylab.ylabel('Moment in debye')
pylab.title('NaH Dipole Moment')
pylab.legend(loc='upper right')


Out[4]:
<matplotlib.legend.Legend at 0x106fdeb70>

In [5]:
pylab.plot(distances, eval_counts, '-o', color=[0.8500, 0.3250, 0.0980], label='VQE')
pylab.xlabel('Interatomic distance')
pylab.ylabel('Evaluations')
pylab.title('VQE number of evaluations')
pylab.legend(loc='upper left')


Out[5]:
<matplotlib.legend.Legend at 0x10f2b8ac8>

In [ ]: