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 RY

from qiskit_chemistry import FermionicOperator
from qiskit_chemistry.drivers import PyQuanteDriver, UnitsType, BasisType

# using driver to get fermionic Hamiltonian
# PyQuante example
driver = PyQuanteDriver(atoms='H .0 .0 .0; H .0 .0 0.735', units=UnitsType.ANGSTROM,
                        charge=0, multiplicity=1, basis=BasisType.BSTO3G)
molecule = driver.run()
h1 = molecule.one_body_integrals
h2 = molecule.two_body_integrals

# convert from fermionic hamiltonian to qubit hamiltonian
ferOp = FermionicOperator(h1=h1, h2=h2)
qubitOp_jw = ferOp.mapping(map_type='JORDAN_WIGNER', threshold=0.00000001)
qubitOp_pa = ferOp.mapping(map_type='PARITY', threshold=0.00000001)
qubitOp_bi = ferOp.mapping(map_type='BRAVYI_KITAEV', threshold=0.00000001)

# print out qubit hamiltonian in Pauli terms and exact solution

qubitOp_jw.to_matrix()
qubitOp_jw.chop(10**-10)

print(qubitOp_jw.print_operators())

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

# 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 = RY(qubitOp_jw.num_qubits, 5, entangler_map = {0: [1], 1:[2], 2:[3]})

# setup VQE with operator, variational form, and optimizer
vqe_algorithm = VQE(qubitOp_jw, 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]))
print("Parameters: {}".format(results['opt_params']))