%run init.ipynb

N = 2**10; th = asin(1/sqrt(N)); N, float(th), float(sin(th)), float(th*180/pi), sqrt(N)

my_list = [1,3,5,2,4,9,5,8,0,7,6]

def my_oracle(my_input):
    solution = 2
    if my_input == solution:
        response = True
    else:
        response = False
    return response

my_input = 7; res = my_oracle(my_input); print(res)

for j in range(0,len(my_list)):
    if my_oracle(my_list[j]) == True:
        print('Solução encontrada no índice',j, ', após ', j+1, ' consultas ao oráculo.')

from qiskit import *
nshots = 8192
qiskit.IBMQ.load_account()
provider = qiskit.IBMQ.get_provider(hub='ibm-q', group='open', project='main')
simulator = Aer.get_backend('qasm_simulator')
device = provider.get_backend('ibmq_belem')
from qiskit.tools.monitor import job_monitor
from qiskit.tools.visualization import plot_histogram

def qc_Uz(n): # 2|0><0| - I
    qc = QuantumCircuit(n, name = 'Uz')
    qrl = []
    for j in range(0, n):
        qrl.append(j)
    qc.z(0); qc.x(0); qc.z(0); qc.x(0)
    qc.x(qrl) # 00...0 -> 11...1 
    qc.h(n-1); qc.mcx(qrl[0:n-1],n-1); qc.h(n-1) # Cz multicontrolada. Multiplica 11...1 por -1
    qc.x(qrl); # 11...1 -> 00...0
    return qc
qc_Uz_ = qc_Uz(3); qc_Uz_.draw()

def qc_Uh(n): # 2|h><h| - I
    qc = QuantumCircuit(n, name = 'Uh')
    qrl = []
    for j in range(0, n):
        qrl.append(j)
    qc.h(qrl) # transforma Uz em Uh
    qc_Uz_ = qc_Uz(n); qc.append(qc_Uz_, qrl)
    qc.h(qrl)
    return qc
qc_Uh_ = qc_Uh(3); qc_Uh_.draw()

def qc_oracle(n):
    # Nielsen & Chuang, Box 6.1 (N=4)
    qc = QuantumCircuit(n+1, name = 'Oracle')
    qc.ccx(0,1,2) # para a solução sendo o estado |11>
    return qc
qc_oracle_ = qc_oracle(2)
qc_oracle_.draw('mpl')

def qc_grover_iteration(n):
    qc = QuantumCircuit(n+1, name = 'G')
    qrl = []
    for j in range(0, n+1):
        qrl.append(j)
    qc_oracle_ = qc_oracle(n)
    qc.append(qc_oracle_, qrl)
    qc_Uh_ = qc_Uh(n); qc.append(qc_Uh_, qrl[0:n])
    return qc
qc_grover_iteration_ = qc_grover_iteration(2)
qc_grover_iteration_.draw('mpl')

def qc_hadamard_state(n): # aplica a Hadamard em n qubits
    qc = QuantumCircuit(n, name = 'H state') # começarei a não usar mais os qr[j]
    qrl = [] # lista de registros quânticos
    for j in range(0, n):
        qrl.append(j)
    qc.h(qrl) # aplica a hadamard em toda a lista de registros quânticos
    return qc
qc_hadamard_state_ = qc_hadamard_state(3); qc_hadamard_state_.draw()

def qc_grover_search(n):
    qc = QuantumCircuit(n+1, n)
    qc.x(n)
    qrl = [] # lista de registros quânticos
    for j in range(0, n+1):
        qrl.append(j)
    qc_hadamard_state_ = qc_hadamard_state(n+1); qc.append(qc_hadamard_state_, qrl)
    for j in range(0, int((math.pi*math.sqrt(2**n))/4 - 1/2)):
        qc_grover_iteration_ = qc_grover_iteration(n); qc.append(qc_grover_iteration_, qrl)
    qc.measure(qrl[0:n], qrl[0:n])
    return qc
qc_grover_search_ = qc_grover_search(2); qc_grover_search_.draw()

qc_grover_search_ = qc_grover_search(2)
result = execute(qc_grover_search_, backend = simulator, shots = nshots).result()
plot_histogram(result.get_counts(qc_grover_search_))

job = execute(qc_grover_search_, backend = device, shots = nshots)
job_monitor(job)

plot_histogram(job.result().get_counts(qc_grover_search_))

