import numpy as np
import pylab

from qiskit import Aer, IBMQ
from qiskit.aqua import QuantumInstance, aqua_globals
from qiskit.aqua.algorithms import VQE, NumPyMinimumEigensolver
from qiskit.aqua.components.optimizers import SPSA
from qiskit.circuit.library import TwoLocal
from qiskit.aqua.operators import WeightedPauliOperator


pauli_dict = {
    'paulis': [{"coeff": {"imag": 0.0, "real": -1.052373245772859}, "label": "II"},
              {"coeff": {"imag": 0.0, "real": 0.39793742484318045}, "label": "ZI"},
              {"coeff": {"imag": 0.0, "real": -0.39793742484318045}, "label": "IZ"},
              {"coeff": {"imag": 0.0, "real": -0.01128010425623538}, "label": "ZZ"},
              {"coeff": {"imag": 0.0, "real": 0.18093119978423156}, "label": "XX"}
              ]
}

qubit_op = WeightedPauliOperator.from_dict(pauli_dict)
num_qubits = qubit_op.num_qubits
print('Number of qubits: {}'.format(num_qubits))

ee = NumPyMinimumEigensolver(qubit_op.copy())
result = ee.run()
ref = result.eigenvalue.real
print('Reference value: {}'.format(ref))

backend = Aer.get_backend('qasm_simulator')
quantum_instance = QuantumInstance(backend=backend, seed_simulator=167, seed_transpiler=167) 

counts = []
values = []
def store_intermediate_result(eval_count, parameters, mean, std):
    counts.append(eval_count)
    values.append(mean)

aqua_globals.random_seed = 167
optimizer = SPSA(maxiter=200)
var_form = TwoLocal(num_qubits, 'ry', 'cz')
vqe = VQE(qubit_op, var_form, optimizer, callback=store_intermediate_result)
vqe_result = vqe.run(quantum_instance)
print('VQE on Aer qasm simulator (no noise): {}'.format(vqe_result.eigenvalue.real))
print('Delta from reference: {}'.format(vqe_result.eigenvalue.real-ref))

pylab.rcParams['figure.figsize'] = (12, 4)
pylab.plot(counts, values)
pylab.xlabel('Eval count')
pylab.ylabel('Energy')
pylab.title('Convergence with no noise')

import os
from qiskit.providers.aer.noise import NoiseModel
from qiskit.providers.ibmq.exceptions import IBMQAccountCredentialsNotFound

backend = Aer.get_backend('qasm_simulator')
counts1 = []
values1 = []
noise_model = None
try:
    os.environ['QISKIT_IN_PARALLEL'] = 'TRUE'
    provider = IBMQ.load_account()
    device = provider.get_backend('ibmqx2')
    coupling_map = device.configuration().coupling_map
    noise_model = NoiseModel.from_backend(device)
    basis_gates = noise_model.basis_gates

    print(noise_model)

    quantum_instance = QuantumInstance(backend=backend, seed_simulator=167, seed_transpiler=167,
                                       noise_model=noise_model,)

    def store_intermediate_result1(eval_count, parameters, mean, std):
        counts1.append(eval_count)
        values1.append(mean)

    aqua_globals.random_seed = 167
    optimizer = SPSA(maxiter=200)
    var_form = TwoLocal(num_qubits, 'ry', 'cz')
    vqe = VQE(qubit_op, var_form, optimizer, callback=store_intermediate_result1)
    vqe_result1 = vqe.run(quantum_instance)
    print('VQE on Aer qasm simulator (with noise): {}'.format(vqe_result1.eigenvalue.real))
    print('Delta from reference: {}'.format(vqe_result1.eigenvalue.real-ref))
except IBMQAccountCredentialsNotFound as ex:
    print(str(ex))

if counts1 or values1:
    pylab.rcParams['figure.figsize'] = (12, 4)
    pylab.plot(counts1, values1)
    pylab.xlabel('Eval count')
    pylab.ylabel('Energy')
    pylab.title('Convergence with noise')

from qiskit.ignis.mitigation.measurement import CompleteMeasFitter

counts1 = []
values1 = []
if noise_model is not None:
    quantum_instance = QuantumInstance(backend=backend, seed_simulator=167, seed_transpiler=167,
                                       noise_model=noise_model, 
                                       measurement_error_mitigation_cls=CompleteMeasFitter, 
                                       cals_matrix_refresh_period=30)

    def store_intermediate_result1(eval_count, parameters, mean, std):
        counts1.append(eval_count)
        values1.append(mean)

    aqua_globals.random_seed = 167
    optimizer = SPSA(maxiter=200)
    var_form = TwoLocal(num_qubits, 'ry', 'cz')
    vqe = VQE(qubit_op, var_form, optimizer, callback=store_intermediate_result1)
    vqe_result1 = vqe.run(quantum_instance)
    print('VQE on Aer qasm simulator (with noise and measurement error mitigation): {}'.format(vqe_result1.eigenvalue))
    print('Delta from reference: {}'.format(vqe_result1.eigenvalue.real-ref))

if counts1 or values1:
    pylab.rcParams['figure.figsize'] = (12, 4)
    pylab.plot(counts1, values1)
    pylab.xlabel('Eval count')
    pylab.ylabel('Energy')
    pylab.title('Convergence with noise, enabling measurement error mitigation')