%run init.ipynb
matplotlib.rcParams.update({'font.size':12}); plt.figure(figsize = (5,3), dpi = 100)
p = np.arange(0,1.01,0.01)
plt.plot(p, p, label = r'$p$'); plt.plot(p, 3*p**2-2*p**3, label = r'$p_{e}$')
plt.xlim(0,1); plt.ylim(-0.01,1.01); plt.legend(); plt.xlabel('p'); plt.show()

from qiskit import *

def qc_coding_bf(th, ph):
    qc = QuantumCircuit(3, name = 'Coding BF')
    qc.u(th, ph, 0, 0)
    qc.barrier()
    qc.cx(0,1); qc.cx(1,2)
    return qc
qc_coding_bf_ = qc_coding_bf(math.pi, math.pi/4); qc_coding_bf_.draw(output = 'mpl')

qc = QuantumCircuit(3, 1); qc.cx([0], [2]); qc.cx([1], [2]); qc.measure([2], 0); qc.draw(output = 'mpl')

qc = QuantumCircuit(5, 2); qc.u(0.1, 0.2, 0.2, [0]); qc.cx([0], [1]); qc.cx([1], [2]); qc.barrier()
qc.cx([0], [3]); qc.cx([1], [3]); qc.cx([1], [4]); qc.cx([2], [4]); qc.barrier(); qc.measure([3, 4], [0, 1])
qc.draw(output = 'mpl')

def qc_ec_bf(th, ph, lb, j):
    qc = QuantumCircuit(5, name = 'EC_BF')
    qc.u(th, ph, lb, [0]) # qubit state preparation
    qc.barrier()
    qc.cx([0], [1]); qc.cx([1], [2]) # encoding
    qc.barrier()
    if j == 0 or j == 1 or j == 2:  # error
        qc.x([j])
    qc.barrier() 
    qc.cx([0], [3]); qc.cx([1], [3]); qc.cx([1], [4]); qc.cx([2], [4]) # syndrome detection
    qc.barrier()
    qc.x([3]); qc.ccx([4], [3], [2]); qc.x([3]); qc.ccx([4], [3], [1]) # correction
    qc.x([4]); qc.ccx([4], [3], [0]); qc.x([4]) # correction
    qc.barrier()
    return qc
th, ph, lb = math.pi, 0.0, 0.0; j = 2; qc_ec_bf_ = qc_ec_bf(th, ph, lb, j)
qc_ec_bf_.draw(output = 'mpl')

from qiskit import *
nshots = 8192
qiskit.IBMQ.load_account()
#provider = IBMQ.get_provider(hub='ibm-q-community', group='ibmquantumawards', project='open-science-22')
#device = provider.get_backend('ibmq_jakarta')
provider = qiskit.IBMQ.get_provider(hub = 'ibm-q-research-2', group = 'federal-uni-sant-1', project = 'main')
device = provider.get_backend('ibmq_bogota')
simulator = Aer.get_backend('qasm_simulator')
from qiskit.tools.monitor import job_monitor
from qiskit.ignis.verification.tomography import state_tomography_circuits, StateTomographyFitter
from qiskit.tools.monitor import backend_overview, backend_monitor
from qiskit.tools.visualization import plot_histogram

qc = QuantumCircuit(5, 3); 
j = 0 # sets the error
qc_ec_bf_ = qc_ec_bf(math.pi/2, 0.0, 0.0, j); qc.append(qc_ec_bf_, [0, 1, 2, 3, 4])
qc.measure([0, 1, 2], [0, 1, 2])
qc.decompose().draw(output = 'mpl')

job_sim = execute(qc, backend = simulator, shots = nshots).result()
plot_histogram(job_sim.get_counts(qc))

job_exp = execute(qc, backend = device, shots = nshots)
print(job_exp.job_id()); job_monitor(job_exp)
plot_histogram(job_exp.result().get_counts(qc))

qc = QuantumCircuit(2, 1)
qc.u(1,2,3, [1])
qc.barrier()
qc.h([0]); qc.cz(0, 1); qc.h([0]); 
qc.measure(0,0); qc.draw(output = 'mpl')

qc = QuantumCircuit(2, 1); 
qc.u(1,2,3, 1); 
qc.barrier()
qc.cx(1,0); qc.measure(0,0); qc.draw(output = 'mpl')

qc = QuantumCircuit(2, 1); 
qc.u(1,2,3, 1)
qc.barrier()
qc.h(0); qc.cx(0,1); qc.h(0); qc.measure(0,0); 
qc.draw(output = 'mpl')

qc = QuantumCircuit(2, 1); 
qc.u(1,2,3, [1]); 
qc.barrier()
qc.h(1); qc.cx(1,0); qc.h(1); qc.measure(0,0); 
qc.draw(output = 'mpl')

qc = QuantumCircuit(2, 1); 
qc.u(0.1, 0.2, 0.3, [1])
qc.barrier()
qc.h(0); qc.cu(0.4, 0.5, 0.6, 0.0, [0], [1]); qc.h([0]); qc.measure([0], [0])
qc.draw(output = 'mpl')

qc = QuantumCircuit(2, 1)
th, ph, lb = math.pi, 0.0, 0.0; qc.u(th, ph, lb, [0]) # state preparation
qc.barrier(); qc.cx([0], [1]); qc.barrier()
qc.x([0]); qc.barrier()
qc.cx([0], [1]); qc.cx([1], [0])
qc.measure([0], [0])
qc.draw(output = 'mpl')

job = execute(qc, backend = simulator, shots = nshots)
plot_histogram(job.result().get_counts(qc))

job = execute(qc, backend = device, shots = nshots); job_monitor(job)
plot_histogram(job.result().get_counts(qc))

# quantum circuit for bit flip error mitigation
def qc_em_bf():
    qc = QuantumCircuit(2, name = 'EM_BF')
    qc.cx([0], [1]); qc.cx([0], [1]); qc.cx([1], [0]); qc.reset([1])
    return qc
qc_em_bf_ = qc_em_bf(); qc_em_bf_.draw(output = 'mpl')

N = 5; qc = QuantumCircuit(1, N); qc.x([0]); 
for j in range(0, N):
    qc.id([0]); qc.measure([0],[j])
job = execute(qc, backend = simulator, shots = nshots)
qc.draw(output = 'mpl')

plot_histogram(job.result().get_counts(qc))

job = execute(qc, backend = device, shots = nshots)
job_monitor(job)
plot_histogram(job.result().get_counts(qc))

N = 5; qc = QuantumCircuit(2, N); qc.x([0]); 
for j in range(0, N):
    qc_em_bf_ = qc_em_bf(); qc.append(qc_em_bf_, [0, 1]); qc.measure([0],[j])
job = execute(qc, backend = simulator, shots = nshots)
qc.decompose().draw(output = 'mpl')

plot_histogram(job.result().get_counts(qc))

job = execute(qc, backend = device, shots = nshots)
job_monitor(job)
plot_histogram(job.result().get_counts(qc))

import math
qc = QuantumCircuit(2, 1)
th, ph, lb = math.pi, 0.0, 0.0; qc.u(th, ph, lb, [0]) # state preparation
qc.barrier(); qc.cx([0], [1]); qc.barrier()
qc.z([0]); qc.x([0]); qc.h([0]); qc.barrier()
qc.cx([0], [1]); qc.cx([1], [0]); qc.measure([0], [0])
qc.draw(output = 'mpl')