from qiskit import QuantumCircuit, QuantumRegister, Aer, execute, ClassicalRegister
from qiskit.ignis.verification.topological_codes import RepetitionCode
from qiskit.ignis.verification.topological_codes import lookuptable_decoding
from qiskit.ignis.verification.topological_codes import GraphDecoder
from qiskit.providers.aer.noise import NoiseModel
from qiskit.providers.aer.noise.errors import pauli_error, depolarizing_error
from qiskit.ignis.mitigation.measurement import (complete_meas_cal,CompleteMeasFitter)
from qiskit.visualization import plot_histogram
backend = Aer.get_backend('qasm_simulator')
#These two functions were taken from https://stackoverflow.com/questions/10237926/convert-string-to-list-of-bits-and-viceversa

def tobits(s):
    result = []
    for c in s:
        bits = bin(ord(c))[2:]
        bits = '00000000'[len(bits):] + bits
        result.extend([int(b) for b in bits])
    return ''.join([str(x) for x in result])

def frombits(bits):
    chars = []
    for b in range(int(len(bits) / 8)):
        byte = bits[b*8:(b+1)*8]
        chars.append(chr(int(''.join([str(bit) for bit in byte]), 2)))
    return ''.join(chars)

def get_noise(p):

    error_meas = pauli_error([('X',p), ('I', 1 - p)])
    noise_model = NoiseModel()
    noise_model.add_all_qubit_quantum_error(error_meas, "measure") 
        
    return noise_model

def codificate(bitString):
    qubits = list()
    for i in range(len(bitString)):
        mycircuit = QuantumCircuit(1,1)
        if(bitString[i] == "1"):
            mycircuit.x(0)
        qubits.append(mycircuit)
    return qubits

def count(array):
    numZero = 0
    numOne = 0
    for i in array:
        if i == "0":
            numZero += 1 
        elif i == "1":
            numOne += 1
    return dict({"0":numZero, "1":numOne})

m0 = tobits("I like dogs") 
qubits = codificate(m0)
measurements = list()
for i in range(len(qubits)):
    qubit = qubits[i]
    qubit.measure(0,0)
    result = execute(qubit, backend, shots=1, memory=True).result()
    measurements.append(int(result.get_memory()[0]))
print(frombits(measurements))

m0 = tobits("I like dogs") 
qubits = codificate(m0)
measurements = list()
for i in range(len(qubits)):
    qubit = qubits[i]
    qubit.measure(0,0)
    result = execute(qubit, backend, shots=1, memory=True, noise_model=get_noise(0.2)).result()
    measurements.append(int(result.get_memory()[0]))
print(frombits(measurements))

for state in ['0','1']:
    qc = QuantumCircuit(1,1)
    if state[0]=='1':
        qc.x(0)  
    qc.measure(qc.qregs[0],qc.cregs[0])
    print(state+' becomes',
          execute(qc, Aer.get_backend('qasm_simulator'),noise_model=get_noise(0.2),shots=1000).result().get_counts())

qubit = qubits[0]
result = execute(qubit, backend, shots=1000, memory=True, noise_model=get_noise(0.2)).result()
print(result.get_counts())

import numpy as np
import scipy.linalg as la

M = [[0.389,0.611],
    [0.593,0.407]]
Cnoisy = [[397],[603]]

Minv = la.inv(M)

Cmitigated = np.dot(Minv, Cnoisy)
print('C_mitigated =\n',Cmitigated)

Cmitigated = np.floor(Cmitigated)
if(Cmitigated[1][0] < 0):
    Cmitigated[1][0] = 0
print('C_mitigated =\n',Cmitigated)

qr = QuantumRegister(1)
meas_calibs, state_labels = complete_meas_cal(qr=qr, circlabel='mcal')
backend = Aer.get_backend('qasm_simulator')
job = execute(meas_calibs, backend=backend, shots=1000,noise_model=get_noise(0.2))
cal_results = job.result()
meas_fitter = CompleteMeasFitter(cal_results, state_labels, circlabel='mcal')
print(meas_fitter.cal_matrix)

qubit = qubits[2]
qubit.measure(0,0)
results = execute(qubit, backend=backend, shots=10000, noise_model=get_noise(0.2),memory=True).result()
noisy_counts = count(results.get_memory())
print(noisy_counts)

Minv = la.inv(meas_fitter.cal_matrix)

Cmitigated = np.dot(Minv, np.array(list(noisy_counts.values())))
print('C_mitigated =\n',Cmitigated)

outputs = list()
for i in range(len(qubits)):
    qubit = qubits[i]
    qubit.measure(0,0)
    results = execute(qubit, backend=backend, shots=10000, memory=True, noise_model=get_noise(0.2)).result()
    noisy_counts = count(results.get_memory())
    Minv = la.inv(meas_fitter.cal_matrix)
    Cmitigated = np.dot(Minv, np.array(list(noisy_counts.values())))
    if(Cmitigated[0] > Cmitigated[1]):
        outputs.append('0')
    else:
        outputs.append('1')
print(frombits(''.join(outputs)))

outputs = list()
meas_filter = meas_fitter.filter
for i in range(len(qubits)):
    qubit = qubits[i]
    qubit.measure(0,0)
    results = execute(qubit, backend=backend, shots=10000, memory=True, noise_model=get_noise(0.2)).result()
    noisy_counts = count(results.get_memory())
    # Results with mitigation
    mitigated_counts = meas_filter.apply(noisy_counts)
    if('1' not in mitigated_counts):
        outputs.append('0')
    elif('0' not in mitigated_counts):
        outputs.append('1')
    elif(mitigated_counts['0'] > mitigated_counts['1']):
        outputs.append('0')
    else:
        outputs.append('1')
print(frombits(''.join(outputs)))