import numpy as np
from qiskit import Aer
from qiskit.transpiler import PassManager

from qiskit_aqua import Operator, QuantumInstance
from qiskit_aqua.algorithms.adaptive import VQE
from qiskit_aqua.algorithms.classical import ExactEigensolver
from qiskit_aqua.components.optimizers import L_BFGS_B
from qiskit_aqua.components.variational_forms import RYRZ

from qiskit_chemistry import FermionicOperator
from qiskit_chemistry.drivers import PySCFDriver, UnitsType

# using driver to get fermionic Hamiltonian
# PySCF example
driver = PySCFDriver(atom='H .0 .0 .0; H .0 .0 0.735', unit=UnitsType.ANGSTROM,
                     charge=0, spin=0, basis='sto3g')
molecule = driver.run()

# get fermionic operator and mapping to qubit operator
ferOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals)
qubitOp = ferOp.mapping(map_type='JORDAN_WIGNER', threshold=0.00000001)
qubitOp.to_matrix()
qubitOp.chop(10**-10)

# If you do not install any driver and would like to start with a random Hamiltonian
# SIZE=4
# matrix = np.random.random((SIZE,SIZE))
# qubitOp = Operator(matrix=matrix)

# Using exact eigensolver to get the smallest eigenvalue
exact_eigensolver = ExactEigensolver(qubitOp, k=1)
ret = exact_eigensolver.run()
print('The exact ground state energy is: {}'.format(ret['eigvals'][0].real))

# setup VQE 
# setup optimizer, use L_BFGS_B optimizer for example
lbfgs = L_BFGS_B(maxfun=1000, factr=10, iprint=10)

# setup variational form generator (generate trial circuits for VQE)
var_form = RYRZ(qubitOp.num_qubits, 5, entangler_map = {0: [1], 1:[2], 2:[3]})

# setup VQE with operator, variational form, and optimizer
vqe_algorithm = VQE(qubitOp, var_form, lbfgs, 'matrix')

backend = Aer.get_backend('statevector_simulator')
quantum_instance = QuantumInstance(backend, pass_manager=PassManager())

results = vqe_algorithm.run(quantum_instance)
print("Minimum value: {}".format(results['eigvals'][0].real))
print("Parameters: {}".format(results['opt_params']))