from qiskit import *
from qiskit.compiler import *
from qiskit.tools.jupyter import *
from qiskit.visualization import *
import matplotlib.pyplot as plotter
import numpy as np
from IPython.display import display, Math, Latex
%matplotlib inline
# Loading your IBM Q account(s)
provider = IBMQ.load_account()

qc1=QuantumCircuit(2)
qc1.h(0)
qc1.h(1)
qc1.barrier() #The part between the barriers is our oracle
qc1.cz(0,1)
#We are using a controlled-Z gate which flips the sign of the second qubit when both qubits are set to '1'
qc1.barrier()
qc1.draw('mpl')

def final_vector(qc):
    backend = Aer.get_backend('statevector_simulator')
    job = execute(qc, backend)
    result = job.result()
    outputstate = result.get_statevector(qc, decimals=3)
    return outputstate
print(final_vector(qc1)) #We can see that the desired state has been obtained

qc2=QuantumCircuit(3)
qc2.h(0)
qc2.h(1)
qc2.h(2)
qc2.barrier()
#The part between the barriers is our oracle

qc2.x(0)
qc2.h(1)
qc2.x(2)
qc2.ccx(0, 2, 1)
qc2.x(0)
qc2.h(1)
qc2.x(2)

qc2.barrier()

qc2.draw('mpl')

def final_vector(qc):
    backend = Aer.get_backend('statevector_simulator')
    job = execute(qc, backend)
    result = job.result()
    outputstate = result.get_statevector(qc, decimals=3)
    return outputstate
print(final_vector(qc2))

def ccz_gate(qc,a,b,c):
    qc.h(c)
    qc.ccx(a,b,c)
    qc.h(c)

#Let's create an oracle which will mark the states 101 and 110
#(This particular oracle won't be using ancilla qubits)
def phase_oracle(circuit):
    circuit.cz(0, 2)
    circuit.cz(0, 1)
n=3
qc2=QuantumCircuit(n,n)
for i in range(0,n):
    qc2.h(i)
qc2.barrier([0,1,2])
#This creates a superposition of all states

#We will now perform the Grover iteration
phase_oracle(qc2)
qc2.barrier([0,1,2])
for i in range(0,n):
    qc2.h(i)

#Performing a conditional phase shift
qc2.x(0)
qc2.x(1)
qc2.x(2)
ccz_gate(qc2,0,1,2)
qc2.x(0)
qc2.x(1)
qc2.x(2)
for i in range(0,n):
    qc2.h(i)
#The Grover iteration is now complete

qc2.barrier([0,1,2])
qc2.draw('mpl')

qc2.measure(0,0)
qc2.measure(1,1)
qc2.measure(2,2)
qc2.draw('mpl')

def counts_circ(circ):
    backend_sim = Aer.get_backend('qasm_simulator')
    job_sim = execute(circ, backend_sim, shots=2000)
    result_sim = job_sim.result()
    counts = result_sim.get_counts(circ)
    return(counts)
plot_histogram(counts_circ(qc2))

qc_mct=QuantumCircuit(5,5)
for i in range(0,4):
    qc_mct.x(i)
qc_mct.mct([0,1,2,3],4)
qc_mct.draw('mpl')

qc_mct.measure(0,0)
qc_mct.measure(1,1)
qc_mct.measure(2,2)
qc_mct.measure(3,3)
qc_mct.measure(4,4)
plot_histogram(counts_circ(qc_mct))

def c3z_gate(qc, a, b, c, d):
    qc.h(d)
    qc.mct([a, b, c], d)
    qc.h(d)

def phase_oracle(qc):
    qc.x(1)
    qc.x(3)
    c3z_gate(qc, 0, 1, 2, 3)   # Handles 1010
    qc.x(2)
    c3z_gate(qc, 0, 1, 2, 3)   # Handles 1000
    qc.x(3)
    c3z_gate(qc, 0, 1, 2, 3)   # Handles 1001
    qc.x(2)
    qc.x(0)
    qc.x(3)
    c3z_gate(qc, 0, 1, 2, 3)   # Handles 0010
    qc.x(0)
    qc.x(1)
    qc.x(3)
    

n = 4
qc3 = QuantumCircuit(n, n)

for i in range(0,n):
    qc3.h(i)
    
qc3.barrier([0, 1, 2, 3])

phase_oracle(qc3)

qc3.barrier([0, 1, 2, 3])

for i in range(0,n):
    qc3.h(i)

qc3.x(0)
qc3.x(1)
qc3.x(2)
qc3.x(3)
c3z_gate(qc3, 0, 1, 2, 3)
qc3.x(0)
qc3.x(1)
qc3.x(2)
qc3.x(3)

for i in range(0,n):
    qc3.h(i)

qc3.barrier([0, 1, 2, 3])

qc3.measure(0, 0)
qc3.measure(1, 1)
qc3.measure(2, 2)
qc3.measure(3, 3)

qc3.draw('mpl')

def counts_circ(circ):
    backend_sim = Aer.get_backend('qasm_simulator')
    job_sim = execute(circ, backend_sim, shots=2000)
    result_sim = job_sim.result()
    counts = result_sim.get_counts(circ)
    return(counts)

plot_histogram(counts_circ(qc3))