import numpy as np
import math
import qiskit as qiskit
from numpy import sqrt
from random import randint
from qiskit import *
from qiskit import Aer, QuantumCircuit, IBMQ, execute, quantum_info, transpile
from qiskit.visualization import plot_state_city, plot_bloch_multivector
from qiskit.visualization import plot_histogram
from qiskit.tools import job_monitor
from qiskit.providers.fake_provider import FakeOpenPulse2Q, FakeOpenPulse3Q, FakeManila, FakeValencia, FakeHanoi
from qiskit import pulse, transpile
from qiskit.pulse.library import Gaussian

#Compuertas personalizadas
from qiskit.circuit import Gate
from qiskit import QiskitError

#informacion
import qiskit.tools.jupyter

provider = IBMQ.load_account()
belem = provider.get_backend('ibmq_belem') 
print('se ha ejecutado correctamente')


def SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    return qc

def SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    return qc

def SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_1)
    return qc

def SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    return qc

def random_oracle(qc, x_qubit_0,x_qubit_1,y_qubit):
    rand=randint(0,3)
    if rand==3:
    	SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==2:
    	SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==1:
    	SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit)
    else:
    	SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit)         
    return qc 


def Grover_Iteration(qc, x_qubit_0,x_qubit_1):
    qc.h(range(2))
    qc.x(range(2))
    qc.h(x_qubit_1)
    qc.cx(x_qubit_0,x_qubit_1)
    qc.h(x_qubit_1)
    qc.x(range(2))
    qc.h(range(2))
    
    return qc

print('se ha ejecutado correctamente')

#Simulador de prueba FakeManila
backend = FakeManila() 
x_register=2
y_register=1
measure_register=2
y_position=x_register+y_register-1

circ = QuantumCircuit(x_register+y_register,measure_register)

circ.x(y_position)
circ.barrier()
circ.h(range(x_register+y_register))
circ.barrier()
random_oracle(circ, 0,1,2)
circ.barrier()
Grover_Iteration(circ, 0,1)
circ.measure(range(x_register),range(measure_register))

circ.draw('mpl')


result = execute(circ, backend=FakeManila()).result();
job = backend.run(circ)
counts = job.result().get_counts()
plot_histogram(counts)

backend = FakeManila();

with pulse.build(backend, name='hadamard') as h_q0:
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=3), pulse.drive_channel(0))

h_q0.draw()

backend = FakeManila();

with pulse.build(backend, name='hadamard') as h_q0: #el 'h_q0 es invariable y puede ser nombrado de cualquier forma solo identifica'
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=33.33), pulse.drive_channel(0))

h_q0.draw()

circ.add_calibration( 'h', [0], h_q0)
circ.add_calibration( 'x', [0], h_q0)
circ.add_calibration( 'cx',[0], h_q0)
circ.add_calibration( 'sx',[0], h_q0)
circ.add_calibration( 'id',[0], h_q0)
circ.add_calibration( 'rz',[0], h_q0)
circ.add_calibration( 'reset',[0], h_q0)


backend = FakeManila()

circ1 = transpile(circ, backend)

print(backend.configuration().basis_gates)
circ1.draw('mpl', idle_wires=False)

result = execute(circ1, backend=FakeManila()).result();
job = backend.run(circ1)
counts = job.result().get_counts()
plot_histogram(counts)

def SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    return qc

def SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    return qc

def SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_1)
    return qc

def SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    return qc

def random_oracle(qc, x_qubit_0,x_qubit_1,y_qubit):
    rand=randint(0,3)
    if rand==3:
    	SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==2:
    	SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==1:
    	SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit)
    else:
    	SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit)         
    return qc 


def Grover_Iteration(qc, x_qubit_0,x_qubit_1):
    qc.h(range(2))
    qc.x(range(2))
    qc.h(x_qubit_1)
    qc.cx(x_qubit_0,x_qubit_1)
    qc.h(x_qubit_1)
    qc.x(range(2))
    qc.h(range(2))
    
    return qc

print('se ha ejecutado correctamente')

#Simulador de prueba FakeOpenPulse3Q
backend = FakeOpenPulse3Q() 
x_register=2
y_register=1
measure_register=2
y_position=x_register+y_register-1

circ = QuantumCircuit(x_register+y_register,measure_register)

circ.x(y_position)
circ.barrier()
circ.h(range(x_register+y_register))
circ.barrier()
random_oracle(circ, 0,1,2)
circ.barrier()
Grover_Iteration(circ, 0,1)
circ.measure(range(x_register),range(measure_register))

circ.draw('mpl')


result = execute(circ, backend=FakeOpenPulse3Q()).result();
job = backend.run(circ)
counts = job.result().get_counts()
plot_histogram(counts)

backend = FakeOpenPulse3Q();

with pulse.build(backend, name='hadamard') as h_q1:
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=3), pulse.drive_channel(0))

h_q1.draw()

backend = FakeOpenPulse3Q();

with pulse.build(backend, name='hadamard') as h_q1: #el 'h_q1 es invariable y puede ser nombrado de cualquier forma solo identifica'
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=33.33), pulse.drive_channel(0))

h_q1.draw()

circ.add_calibration( 'h', [0], h_q1)
circ.add_calibration( 'x', [0], h_q1)
circ.add_calibration( 'cx',[0], h_q1)
circ.add_calibration( 'sx',[0], h_q1)
circ.add_calibration( 'id',[0], h_q1)
circ.add_calibration( 'rz',[0], h_q1)
circ.add_calibration( 'reset',[0], h_q1)


backend = FakeOpenPulse3Q()

circ2 = transpile(circ, backend)

print(backend.configuration().basis_gates)
circ2.draw('mpl', idle_wires=False)

result = execute(circ2, backend=FakeManila()).result();
job = backend.run(circ2)
counts = job.result().get_counts()
plot_histogram(counts)

def SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    return qc

def SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    return qc

def SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_1)
    return qc

def SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    return qc

def random_oracle(qc, x_qubit_0,x_qubit_1,y_qubit):
    rand=randint(0,3)
    if rand==3:
    	SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==2:
    	SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==1:
    	SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit)
    else:
    	SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit)         
    return qc 


def Grover_Iteration(qc, x_qubit_0,x_qubit_1):
    qc.h(range(2))
    qc.x(range(2))
    qc.h(x_qubit_1)
    qc.cx(x_qubit_0,x_qubit_1)
    qc.h(x_qubit_1)
    qc.x(range(2))
    qc.h(range(2))
    
    return qc

print('se ha ejecutado correctamente')

#Simulador de prueba FakeManila
backend = FakeManila() 
x_register=2
y_register=1
measure_register=2
y_position=x_register+y_register-1

circ = QuantumCircuit(x_register+y_register,measure_register)

circ.x(y_position)
circ.barrier()
circ.h(range(x_register+y_register))
circ.barrier()
random_oracle(circ, 0,1,2)
circ.barrier()
Grover_Iteration(circ, 0,1)
circ.measure(range(x_register),range(measure_register))

circ.draw('mpl')


result = execute(circ, backend=FakeManila()).result();
job = backend.run(circ)
counts = job.result().get_counts()
plot_histogram(counts)

backend = FakeManila();

with pulse.build(backend, name='hadamard') as h_q0:
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=3), pulse.drive_channel(0))

h_q0.draw()

backend = FakeManila();

with pulse.build(backend, name='hadamard') as h_q0:
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=10), pulse.drive_channel(0))

h_q0.draw()

circ.add_calibration( 'h', [0], h_q0)
circ.add_calibration( 'x', [0], h_q0)
circ.add_calibration( 'cx',[0], h_q0)
circ.add_calibration( 'sx',[0], h_q0)
circ.add_calibration( 'id',[0], h_q0)
circ.add_calibration( 'rz',[0], h_q0)
circ.add_calibration( 'reset',[0], h_q0)


backend = FakeManila()

circ1 = transpile(circ, backend)

print(backend.configuration().basis_gates)
circ1.draw('mpl', idle_wires=False)

result = execute(circ1, backend=FakeManila()).result();
job = backend.run(circ1)
counts = job.result().get_counts()
plot_histogram(counts)

def SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    return qc

def SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    return qc

def SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_1)
    return qc

def SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    return qc

def random_oracle(qc, x_qubit_0,x_qubit_1,y_qubit):
    rand=randint(0,3)
    if rand==3:
    	SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==2:
    	SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==1:
    	SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit)
    else:
    	SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit)         
    return qc 


def Grover_Iteration(qc, x_qubit_0,x_qubit_1):
    qc.h(range(2))
    qc.x(range(2))
    qc.h(x_qubit_1)
    qc.cx(x_qubit_0,x_qubit_1)
    qc.h(x_qubit_1)
    qc.x(range(2))
    qc.h(range(2))
    
    return qc

print('se ha ejecutado correctamente')

#Simulador de prueba FakeOpenPulse3Q
backend = FakeOpenPulse3Q() 
x_register=2
y_register=1
measure_register=2
y_position=x_register+y_register-1

circ = QuantumCircuit(x_register+y_register,measure_register)

circ.x(y_position)
circ.barrier()
circ.h(range(x_register+y_register))
circ.barrier()
random_oracle(circ, 0,1,2)
circ.barrier()
Grover_Iteration(circ, 0,1)
circ.measure(range(x_register),range(measure_register))

circ.draw('mpl')


result = execute(circ, backend=FakeOpenPulse3Q()).result();
job = backend.run(circ)
counts = job.result().get_counts()
plot_histogram(counts)

backend = FakeOpenPulse3Q();

with pulse.build(backend, name='hadamard') as h_q1:
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=3), pulse.drive_channel(0))

h_q1.draw()

backend = FakeOpenPulse3Q();

with pulse.build(backend, name='hadamard') as h_q1:
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=10), pulse.drive_channel(0))

h_q1.draw()

circ.add_calibration( 'h', [0], h_q1)
circ.add_calibration( 'x', [0], h_q1)
circ.add_calibration( 'cx',[0], h_q1)
circ.add_calibration( 'sx',[0], h_q1)
circ.add_calibration( 'id',[0], h_q1)
circ.add_calibration( 'rz',[0], h_q1)
circ.add_calibration( 'reset',[0], h_q1)


backend = FakeOpenPulse3Q()

circ2 = transpile(circ, backend)

print(backend.configuration().basis_gates)
circ2.draw('mpl', idle_wires=False)

result = execute(circ2, backend=FakeOpenPulse3Q()).result();
job = backend.run(circ2)
counts = job.result().get_counts()
plot_histogram(counts)

def SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    return qc

def SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    return qc

def SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_1)
    return qc

def SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    return qc

def random_oracle(qc, x_qubit_0,x_qubit_1,y_qubit):
    rand=randint(0,3)
    if rand==3:
    	SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==2:
    	SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==1:
    	SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit)
    else:
    	SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit)         
    return qc 


def Grover_Iteration(qc, x_qubit_0,x_qubit_1):
    qc.h(range(2))
    qc.x(range(2))
    qc.h(x_qubit_1)
    qc.cx(x_qubit_0,x_qubit_1)
    qc.h(x_qubit_1)
    qc.x(range(2))
    qc.h(range(2))
    
    return qc

print('se ha ejecutado correctamente')

#Simulador de prueba FakeManila
backend = FakeManila() 
x_register=2
y_register=1
measure_register=2
y_position=x_register+y_register-1

circ = QuantumCircuit(x_register+y_register,measure_register)

circ.x(y_position)
circ.barrier()
circ.h(range(x_register+y_register))
circ.barrier()
random_oracle(circ, 0,1,2)
circ.barrier()
Grover_Iteration(circ, 0,1)
circ.measure(range(x_register),range(measure_register))

circ.draw('mpl')


result = execute(circ, backend=FakeManila()).result();
job = backend.run(circ)
counts = job.result().get_counts()
plot_histogram(counts)

backend = FakeManila();

with pulse.build(backend, name='hadamard') as h_q0:
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=3), pulse.drive_channel(0))

h_q0.draw()

backend = FakeManila();

with pulse.build(backend, name='hadamard') as h_q0:
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=5), pulse.drive_channel(0))

h_q0.draw()

circ.add_calibration( 'h', [0], h_q0)
circ.add_calibration( 'x', [0], h_q0)
circ.add_calibration( 'cx',[0], h_q0)
circ.add_calibration( 'sx',[0], h_q0)
circ.add_calibration( 'id',[0], h_q0)
circ.add_calibration( 'rz',[0], h_q0)
circ.add_calibration( 'reset',[0], h_q0)


backend = FakeManila()

circ1 = transpile(circ, backend)

print(backend.configuration().basis_gates)
circ1.draw('mpl', idle_wires=False)

result = execute(circ1, backend=FakeManila()).result();
job = backend.run(circ1)
counts = job.result().get_counts()
plot_histogram(counts)

def SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    return qc

def SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    return qc

def SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_1)
    return qc

def SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit):
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    qc.ccx(x_qubit_0,x_qubit_1,y_qubit)
    qc.x(x_qubit_0)
    qc.x(x_qubit_1)
    return qc

def random_oracle(qc, x_qubit_0,x_qubit_1,y_qubit):
    rand=randint(0,3)
    if rand==3:
    	SoQ_Grover_0(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==2:
    	SoQ_Grover_1(qc, x_qubit_0,x_qubit_1,y_qubit)
    elif rand==1:
    	SoQ_Grover_2(qc, x_qubit_0,x_qubit_1,y_qubit)
    else:
    	SoQ_Grover_3(qc, x_qubit_0,x_qubit_1,y_qubit)         
    return qc 


def Grover_Iteration(qc, x_qubit_0,x_qubit_1):
    qc.h(range(2))
    qc.x(range(2))
    qc.h(x_qubit_1)
    qc.cx(x_qubit_0,x_qubit_1)
    qc.h(x_qubit_1)
    qc.x(range(2))
    qc.h(range(2))
    
    return qc

print('se ha ejecutado correctamente')

#Simulador de prueba FakeOpenPulse3Q
backend = FakeOpenPulse3Q() 
x_register=2
y_register=1
measure_register=2
y_position=x_register+y_register-1

circ = QuantumCircuit(x_register+y_register,measure_register)

circ.x(y_position)
circ.barrier()
circ.h(range(x_register+y_register))
circ.barrier()
random_oracle(circ, 0,1,2)
circ.barrier()
Grover_Iteration(circ, 0,1)
circ.measure(range(x_register),range(measure_register))

circ.draw('mpl')


result = execute(circ, backend=FakeOpenPulse3Q()).result();
job = backend.run(circ)
counts = job.result().get_counts()
plot_histogram(counts)

backend = FakeOpenPulse3Q();

with pulse.build(backend, name='hadamard') as h_q1:
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=3), pulse.drive_channel(0))

h_q1.draw()

backend = FakeOpenPulse3Q();

with pulse.build(backend, name='hadamard') as h_q1:
    pulse.play(Gaussian(duration=100, amp=0.1, sigma=5), pulse.drive_channel(0))

h_q1.draw()

circ.add_calibration( 'h', [0], h_q1)
circ.add_calibration( 'x', [0], h_q1)
circ.add_calibration( 'cx',[0], h_q1)
circ.add_calibration( 'sx',[0], h_q1)
circ.add_calibration( 'id',[0], h_q1)
circ.add_calibration( 'rz',[0], h_q1)
circ.add_calibration( 'reset',[0], h_q1)


backend = FakeOpenPulse3Q()

circ2 = transpile(circ, backend)

print(backend.configuration().basis_gates)
circ2.draw('mpl', idle_wires=False)

result = execute(circ2, backend=FakeOpenPulse3Q()).result();
job = backend.run(circ2)
counts = job.result().get_counts()
plot_histogram(counts)

