import numpy as np

# Importing standard Qiskit libraries
from qiskit import QuantumCircuit, transpile, Aer, IBMQ
from qiskit.tools.jupyter import *
from qiskit.visualization import *
from ibm_quantum_widgets import *
from qiskit.providers.aer import QasmSimulator

# Loading your IBM Quantum account(s)
provider = IBMQ.load_account()

from qiskit  import assemble
import numpy as np
import matplotlib.pyplot as plt

n=4
grover_ckt=QuantumCircuit(n+1,n)
marked=[1,0,1,1]#1101 marked =13

def apply_oracle(n,marked,ckt):
    controlO=[i for i in range(n) if not marked[i]]
    ckt.x(controlO)
    ckt.mct(list(range(n)),n)
    ckt.x(controlO)
    ckt.draw()

def reflect_uniform(ckt,n):
    ckt.h(list(range(n)))
    ckt.x(list(range(n)))
    ckt.mct(list(range(n)),n)
    ckt.x(list(range(n+1)))
    ckt.h(list(range(n)))
    

grover_ckt.x(n)
grover_ckt.barrier()
grover_ckt.h(list(range(n+1)))

grover_ckt.draw()

svsim= Aer.get_backend('statevector_simulator')
qobj=assemble(grover_ckt)
result=svsim.run(qobj).result()
#statevector=result.data()["statevector"]
statevector=result.get_statevector()
statevector=np.array(statevector)
#statevector.dtype
statevector.shape
print(statevector)
statevector=statevector[:2**n]
statevector.shape
#print(np.array([i for i in range(2**n)]))
#statevector = statevector(dims=[i for i in range(2**n)])

marked=[1,0,1,1]
ket_a=np.zeros(2**n)
ket_a[13]=1
ket_e=(np.ones(2**n)-ket_a)/np.sqrt(2**n-1)

ket_a

ket_e

def get_projection(psi,e,a):
    proj=[np.real(np.vdot(e,psi)),np.real(np.vdot(a,psi))]
    return proj
def plt_vector(proj,axes=[0.0,1.0,0.0,1.0]):
    x_pos=0
    y_pos=0
    x_direct=proj[0]
    y_direct=proj[1]
    fig,ax =plt.subplots()
    ax.quiver(x_pos,y_pos,x_direct,y_direct,scale=1.0)
    ax.axis(axes) 
    plt.show()
              
              
              
              
        
    

proj =get_projection(statevector,ket_e,ket_a)
plt_vector(proj)

apply_oracle(4,marked,grover_ckt)
grover_ckt.barrier()

grover_ckt.draw()

reflect_uniform(grover_ckt,n)

grover_ckt.barrier()
grover_ckt.draw()

svsim= Aer.get_backend('statevector_simulator')
qobj=assemble(grover_ckt)
result=svsim.run(qobj).result()
#statevector=result.data()["statevector"]
statevector=result.get_statevector()
statevector=np.array(statevector)
statevector=statevector[:2**n]
proj =get_projection(statevector,ket_e,ket_a)
plt_vector(proj)

apply_oracle(4,marked,grover_ckt)
grover_ckt.barrier()
reflect_uniform(grover_ckt,n)
grover_ckt.barrier()
grover_ckt.draw()

svsim= Aer.get_backend('statevector_simulator')
qobj=assemble(grover_ckt)
result=svsim.run(qobj).result()
#statevector=result.data()["statevector"]
statevector=result.get_statevector()
statevector=np.array(statevector)
statevector=statevector[:2**n]
proj =get_projection(statevector,ket_e,ket_a)
plt_vector(proj)

apply_oracle(4,marked,grover_ckt)
grover_ckt.barrier()
reflect_uniform(grover_ckt,n)
grover_ckt.barrier()
grover_ckt.draw()

svsim= Aer.get_backend('statevector_simulator')
qobj=assemble(grover_ckt)
result=svsim.run(qobj).result()
#statevector=result.data()["statevector"]
statevector=result.get_statevector()
statevector=np.array(statevector)
statevector=statevector[:2**n]
proj =get_projection(statevector,ket_e,ket_a)
plt_vector(proj,axes=[-1.0,1.0,-1.0,1.0])

import math
thetaO=math.asin(1/math.sqrt(2**n))
print(thetaO)

T=0
angle=thetaO
print(angle)
while(angle+(2*thetaO)<=(math.pi/2)and angle>=0):
    T+=1
    angle=angle+(2*thetaO)
    print(angle)
   
#print(T)
    

n=4
grover_ckt=QuantumCircuit(n+1,n)
marked=[1,0,1,1]#1101 marked =13
grover_ckt.x(n)
grover_ckt.barrier()
grover_ckt.h(list(range(n+1)))
grover_ckt.barrier()
for _ in range(int(np.floor(T))):
    apply_oracle(4,marked,grover_ckt)
    grover_ckt.barrier()
    reflect_uniform(grover_ckt,n)
    grover_ckt.barrier()
for j in range(n):
    grover_ckt.measure(j,j)
grover_ckt.draw()

sim=Aer.get_backend("qasm_simulator")
qobj=assemble(grover_ckt)
result=sim.run(qobj).result()
result

counts=result.get_counts(grover_ckt)

plot_histogram(counts)

 