In [1]:
# import common packages
from collections import OrderedDict
import itertools
import logging
import numpy as np
import scipy
from qiskit_aqua import (get_algorithm_instance, get_optimizer_instance,
get_variational_form_instance, get_initial_state_instance, Operator)
from qiskit_aqua._logging import build_logging_config, set_logging_config
from qiskit_aqua_chemistry.drivers import ConfigurationManager
from qiskit_aqua_chemistry.core import get_chemistry_operator_instance
# set_logging_config(build_logging_config(logging.INFO))
In [2]:
# using driver to get fermionic Hamiltonian
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]:
core = get_chemistry_operator_instance('hamiltonian')
hamiltonian_cfg = OrderedDict([
('name', 'hamiltonian'),
('transformation', 'full'),
('qubit_mapping', 'parity'),
('two_qubit_reduction', True),
('freeze_core', True),
('orbital_reduction', [])
])
core.init_params(hamiltonian_cfg)
algo_input = core.run(molecule)
qubit_op = algo_input.qubit_op
print("Originally requires {} qubits".format(qubit_op.num_qubits))
print(qubit_op)
Find the symmetries of the qubit operator
In [4]:
[symmetries, sq_paulis, cliffords, sq_list] = qubit_op.find_Z2_symmetries()
print('Z2 symmetries found:')
for symm in symmetries:
print(symm.to_label())
print('single qubit operators found:')
for sq in sq_paulis:
print(sq.to_label())
print('cliffords found:')
for clifford in cliffords:
print(clifford.print_operators())
print('single-qubit list: {}'.format(sq_list))
Use the found symmetries, single qubit operators, and cliffords to taper qubits from the original qubit operator. For each Z2 symmetry one can taper one qubit. However, different tapered operators can be built, corresponding to different symmetry sectors.
In [5]:
tapered_ops = []
for coeff in itertools.product([1, -1], repeat=len(sq_list)):
tapered_op = Operator.qubit_tapering(qubit_op, cliffords, sq_list, list(coeff))
tapered_ops.append((list(coeff), tapered_op))
print("Number of qubits of tapered qubit operator: {}".format(tapered_op.num_qubits))
The user has to specify the symmetry sector he is interested in. Since we are interested in finding the ground state here, let us get the original ground state energy as a reference.
In [6]:
ee = get_algorithm_instance('ExactEigensolver')
ee.init_args(qubit_op, k=1)
result = core.process_algorithm_result(ee.run())
for line in result[0]:
print(line)
Now, let us iterate through all tapered qubit operators to find out the one whose ground state energy matches the original (un-tapered) one.
In [7]:
smallest_eig_value = 99999999999999
smallest_idx = -1
for idx in range(len(tapered_ops)):
ee.init_args(tapered_ops[idx][1], k=1)
curr_value = ee.run()['energy']
if curr_value < smallest_eig_value:
smallest_eig_value = curr_value
smallest_idx = idx
print("Lowest eigenvalue of the {}-th tapered operator (computed part) is {:.12f}".format(idx, curr_value))
the_tapered_op = tapered_ops[smallest_idx][1]
the_coeff = tapered_ops[smallest_idx][0]
print("The {}-th tapered operator matches original ground state energy, with corresponding symmetry sector of {}".format(smallest_idx, the_coeff))
Alternatively, one can run multiple VQE instances to find the lowest eigenvalue sector.
Here we just validate that the_tapered_op
reach the smallest eigenvalue in one VQE execution with the UCCSD variational form, modified to take into account of the tapered symmetries.
In [8]:
# setup initial state
init_state = get_initial_state_instance('HartreeFock')
init_state.init_args(num_qubits=the_tapered_op.num_qubits, num_orbitals=core._molecule_info['num_orbitals'],
qubit_mapping=core._qubit_mapping, two_qubit_reduction=core._two_qubit_reduction,
num_particles=core._molecule_info['num_particles'], sq_list=sq_list)
# setup variationl form
var_form = get_variational_form_instance('UCCSD')
var_form.init_args(num_qubits=the_tapered_op.num_qubits, depth=1,
num_orbitals=core._molecule_info['num_orbitals'],
num_particles=core._molecule_info['num_particles'],
active_occupied=None, active_unoccupied=None, initial_state=init_state,
qubit_mapping=core._qubit_mapping, two_qubit_reduction=core._two_qubit_reduction,
num_time_slices=1,
cliffords=cliffords, sq_list=sq_list, tapering_values=the_coeff, symmetries=symmetries)
# setup optimizer
optimizer = get_optimizer_instance('COBYLA')
optimizer.init_args()
optimizer.set_options(maxiter=1000)
# set vqe
algo = get_algorithm_instance('VQE')
algo.setup_quantum_backend(backend='statevector_simulator')
algo.init_args(the_tapered_op, 'matrix', var_form, optimizer)
In [9]:
algo_result = algo.run()
In [11]:
result = core.process_algorithm_result(algo_result)
for line in result[0]:
print(line)
print("The parameters for UCCSD are:\n{}".format(algo_result['opt_params']))
In [ ]: