from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, Aer, execute, IBMQ
from qiskit.providers.aer import noise
from qiskit.providers.aer.noise import NoiseModel
from qiskit.visualization import plot_histogram
from qiskit.ignis.mitigation.measurement import complete_meas_cal, CompleteMeasFitter

qr = QuantumRegister(5)
qubit_list = [2, 3, 4] # os qubits para os quais aplicaremos calibracao de medidas
meas_calibs, state_labels = complete_meas_cal(qubit_list = qubit_list, qr = qr)

print(state_labels)

meas_calibs # circuitos que serao executados para obter dados para calibracao (uma para cada state_labels)

nshots = 8192
simulator = Aer.get_backend('qasm_simulator')
job = execute(meas_calibs, backend = simulator, shots = nshots) # executa a calibracao
cal_results = job.result()

# a matriz de calibracao sem ruido e a identidade
meas_fitter = CompleteMeasFitter(cal_results, state_labels)
print(meas_fitter.cal_matrix)

meas_fitter.plot_calibration()

provider = IBMQ.load_account()
device = provider.get_backend('ibmq_bogota')
noise_model = NoiseModel.from_backend(device)
basis_gates = noise_model.basis_gates
coupling_map = device.configuration().coupling_map

job = execute(meas_calibs, backend = simulator, shots = nshots, noise_model = noise_model, 
             basis_gates = basis_gates, coupling_map = coupling_map)
cal_results = job.result()

meas_fitter = CompleteMeasFitter(cal_results, state_labels)
print(meas_fitter.cal_matrix)

# visualizacao grafica da matriz de calibracao
meas_fitter.plot_calibration()

qr = QuantumRegister(5); cr = ClassicalRegister(3); qc_ghz = QuantumCircuit(qr, cr)
qc_ghz.h(qr[2]); qc_ghz.cx(qr[2], qr[3]); qc_ghz.cx(qr[3], qr[4])
qc_ghz.measure(qr[2], cr[0]); qc_ghz.measure(qr[3], cr[1]); qc_ghz.measure(qr[4], cr[2]);
qc_ghz.draw(output = 'mpl')

qubit_list = [2, 3, 4] # os qubits para os quais aplicaremos calibracao de medidas
meas_calibs, state_labels = complete_meas_cal(qubit_list = qubit_list, qr = qr)

job = execute(meas_calibs, backend = simulator, shots = nshots, noise_model = noise_model, 
             basis_gates = basis_gates, coupling_map = coupling_map)
cal_results = job.result()

meas_fitter = CompleteMeasFitter(cal_results, state_labels)

job = execute(qc_ghz, backend = simulator, shots = nshots, noise_model = noise_model, 
             basis_gates = basis_gates, coupling_map = coupling_map)
unmitigated_counts = job.result().get_counts() # resultados sem mitigacao de erros

# Resultados com mitigacao de erros
mitigated_results = meas_fitter.filter.apply(job.result())
mitigated_counts = mitigated_results.get_counts()

plot_histogram([unmitigated_counts, mitigated_counts], legend=['sem mit', 'com mit'])

qr = QuantumRegister(3); qc = QuantumCircuit(qr); qc.h([0]); qc.cx([0], [1])
qc.draw(output = 'mpl')

from qiskit.ignis.verification.tomography import state_tomography_circuits, StateTomographyFitter

qstc = state_tomography_circuits(qc, [qr[0], qr[1]])
job = execute(qstc, backend = simulator, shots = nshots)
qstf = StateTomographyFitter(job.result(), qstc)
rho = qstf.fit(method = 'lstsq')
print(rho.real)

from qiskit.visualization import plot_state_city
plot_state_city(rho, title=r'$|\Phi_{+}^{sim}\rangle$')

device = provider.get_backend('ibmq_bogota')
noise_model = NoiseModel.from_backend(device)
basis_gates = noise_model.basis_gates
coupling_map = device.configuration().coupling_map
job = execute(qstc, backend = simulator, shots = nshots, noise_model = noise_model, 
             basis_gates = basis_gates, coupling_map = coupling_map)
qstf = StateTomographyFitter(job.result(), qstc)
rho = qstf.fit(method='lstsq')
print(rho.real)

plot_state_city(rho, title=r'$|\Phi_{+}^{simN}\rangle$')

qubit_list = [0, 1] 
meas_calibs, state_labels = complete_meas_cal(qubit_list = qubit_list, qr = qr)
job = execute(meas_calibs, backend = simulator, shots = nshots, noise_model = noise_model, 
             basis_gates = basis_gates, coupling_map = coupling_map)
cal_results = job.result()
meas_fitter = CompleteMeasFitter(cal_results, state_labels)

qstc = state_tomography_circuits(qc, [qr[0], qr[1]])
job = execute(qstc, backend = simulator, shots = nshots, noise_model = noise_model, 
             basis_gates = basis_gates, coupling_map = coupling_map)
mitigated_results = meas_fitter.filter.apply(job.result())
qstf = StateTomographyFitter(mitigated_results, qstc)
rho = qstf.fit(method = 'lstsq')
print(rho.real)

plot_state_city(rho, title=r'$|\Phi_{+}^{simNcal}\rangle$')

qr = QuantumRegister(3); qc = QuantumCircuit(qr); qc.h([0]); qc.cx([0], [1])
qc.draw(output = 'mpl')

provider = IBMQ.get_provider(hub = 'ibm-q-research-2', group = 'federal-uni-sant-1', project = 'main')
device = provider.get_backend('ibmq_bogota')
from qiskit.tools.monitor import job_monitor

qstc = state_tomography_circuits(qc, [qr[0], qr[1]])
job_exp = execute(qstc, backend = device, shots = nshots); job_monitor(job_exp)
qstf = StateTomographyFitter(job_exp.result(), qstc)
rho = qstf.fit(method = 'lstsq'); print(rho.real)

plot_state_city(rho, title=r'$|\Phi_{+}^{exp}\rangle$')

qubit_list = [0, 1] # os qubits para os quais aplicaremos calibracao de medidas
meas_calibs, state_labels = complete_meas_cal(qubit_list = qubit_list, qr = qr)
job = execute(meas_calibs, backend = device, shots = nshots)
job_monitor(job)
cal_results = job.result()
meas_fitter = CompleteMeasFitter(cal_results, state_labels)

mitigated_results = meas_fitter.filter.apply(job_exp.result())
qstf = StateTomographyFitter(mitigated_results, qstc)
rho = qstf.fit(method = 'lstsq'); print(rho.real)

plot_state_city(rho, title=r'$|\Phi_{+}^{ExpCal}\rangle$')

