The latest version of this notebook is available on https://github.com/Qiskit/qiskit-tutorial.
Richard Chen[1], Antonio Mezzacapo[1], Marco Pistoia[1], Stephen Wood[1]
In the aqua_chemistry_howto example, we show how to configure different parameters in an input dictionary for different experiments in Aqua Chemistry. Nonetheless, for the users who intersted in experimenting with their new algorithm or new components in the algorithm, which is outside of the bounds of the high-level APIs, the users may want to break down the computation into smaller steps.
In this notebook, we decompose the steps to compute the ground state energy of a molecule into 4 steps:
In [1]:
# import common packages
import numpy as np
from collections import OrderedDict
# lib from Qiskit Aqua Chemistry
from qiskit_aqua_chemistry import FermionicOperator
# lib from Qiskit Aqua
from qiskit_aqua import Operator
from qiskit_aqua import (get_algorithm_instance, get_optimizer_instance,
get_variational_form_instance, get_initial_state_instance)
# lib for driver
from qiskit_aqua_chemistry.drivers import ConfigurationManager
In [2]:
# using driver to get fermionic Hamiltonian
# PySCF example
cfg_mgr = ConfigurationManager()
pyscf_cfg = OrderedDict([('atom', 'Li .0 .0 .0; H .0 .0 1.6'),
('unit', 'Angstrom'),
('charge', 0),
('spin', 0),
('basis', 'sto3g')])
section = {}
section['properties'] = pyscf_cfg
driver = cfg_mgr.get_driver_instance('PYSCF')
molecule = driver.run(section)
In [3]:
# please be aware that the idx here with respective to original idx
freeze_list = [0]
remove_list = [-3, -2] # negative number denotes the reverse order
map_type = 'parity'
h1 = molecule._one_body_integrals
h2 = molecule._two_body_integrals
nuclear_repulsion_energy = molecule._nuclear_repulsion_energy
num_particles = molecule._num_alpha + molecule._num_beta
num_spin_orbitals = molecule._num_orbitals * 2
print("HF energy: {}".format(molecule._hf_energy - molecule._nuclear_repulsion_energy))
print("# of electrons: {}".format(num_particles))
print("# of spin orbitals: {}".format(num_spin_orbitals))
# prepare full idx of freeze_list and remove_list
# convert all negative idx to positive
remove_list = [x % molecule._num_orbitals for x in remove_list]
freeze_list = [x % molecule._num_orbitals for x in freeze_list]
# update the idx in remove_list of the idx after frozen, since the idx of orbitals are changed after freezing
remove_list = [x - len(freeze_list) for x in remove_list]
remove_list += [x + molecule._num_orbitals - len(freeze_list) for x in remove_list]
freeze_list += [x + molecule._num_orbitals for x in freeze_list]
# prepare fermionic hamiltonian with orbital freezing and eliminating, and then map to qubit hamiltonian
# and if PARITY mapping is selected, reduction qubits
energy_shift = 0.0
qubit_reduction = True if map_type == 'parity' else False
ferOp = FermionicOperator(h1=h1, h2=h2)
if len(freeze_list) > 0:
ferOp, energy_shift = ferOp.fermion_mode_freezing(freeze_list)
num_spin_orbitals -= len(freeze_list)
num_particles -= len(freeze_list)
if len(remove_list) > 0:
ferOp = ferOp.fermion_mode_elimination(remove_list)
num_spin_orbitals -= len(remove_list)
qubitOp = ferOp.mapping(map_type=map_type, threshold=0.00000001)
qubitOp = qubitOp.two_qubit_reduced_operator(num_particles) if qubit_reduction else qubitOp
qubitOp.chop(10**-10)
We use the classical eigen decomposition to get the smallest eigenvalue as a reference.
In [4]:
# Using exact eigensolver to get the smallest eigenvalue
exact_eigensolver = get_algorithm_instance('ExactEigensolver')
exact_eigensolver.init_args(qubitOp, k=1)
ret = exact_eigensolver.run()
print('The computed energy is: {:.12f}'.format(ret['eigvals'][0].real))
print('The total ground state energy is: {:.12f}'.format(ret['eigvals'][0].real + energy_shift + nuclear_repulsion_energy))
In [5]:
from qiskit import IBMQ
IBMQ.load_accounts()
In [6]:
# setup COBYLA optimizer
max_eval = 200
cobyla = get_optimizer_instance('COBYLA')
cobyla.set_options(maxiter=max_eval)
# setup HartreeFock state
HF_state = get_initial_state_instance('HartreeFock')
HF_state.init_args(qubitOp.num_qubits, num_spin_orbitals, map_type,
qubit_reduction, num_particles)
# setup UCCSD variational form
var_form = get_variational_form_instance('UCCSD')
var_form.init_args(qubitOp.num_qubits, depth=1,
num_orbitals=num_spin_orbitals, num_particles=num_particles,
active_occupied=[0], active_unoccupied=[0, 1],
initial_state=HF_state, qubit_mapping=map_type,
two_qubit_reduction=qubit_reduction, num_time_slices=1)
# setup VQE
vqe_algorithm = get_algorithm_instance('VQE')
vqe_algorithm.setup_quantum_backend(backend='statevector_simulator')
vqe_algorithm.init_args(qubitOp, 'matrix', var_form, cobyla)
In [7]:
results = vqe_algorithm.run()
print('The computed ground state energy is: {:.12f}'.format(results['eigvals'][0]))
print('The total ground state energy is: {:.12f}'.format(results['eigvals'][0] + energy_shift + nuclear_repulsion_energy))
print("Parameters: {}".format(results['opt_params']))