# Useful additional packages
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
from math import pi

from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, execute
from qiskit import Aer, IBMQ
from qiskit.compiler import transpile, assemble
from qiskit.visualization import *
from qiskit.providers.aer import UnitarySimulator
from qiskit.tools.visualization import circuit_drawer
from qiskit.quantum_info import state_fidelity
from qiskit import BasicAer


backend = BasicAer.get_backend('unitary_simulator')

qr = QuantumRegister(2)
cr = ClassicalRegister(2)
groverCircuit = QuantumCircuit(qr,cr)

groverCircuit.h(qr)
groverCircuit.draw(output="mpl")

job = execute(groverCircuit, backend)
job.result().get_unitary(groverCircuit, decimals=3)

groverCircuit.measure(qr,cr)
groverCircuit.draw(output="mpl")

simulator = Aer.get_backend('qasm_simulator')
result = execute(groverCircuit, simulator).result()
counts = result.get_counts(groverCircuit)
plot_histogram(counts, title='Grover Intermediate measurement')

qr = QuantumRegister(2)
cr = ClassicalRegister(2)
groverCircuit = QuantumCircuit(qr,cr)
groverCircuit.h(qr)

'''oracle w = |00>'''
groverCircuit.x(qr)
groverCircuit.cz(qr[0],qr[1])
groverCircuit.x(qr)

groverCircuit.draw(output="mpl")

job = execute(groverCircuit, backend)
job.result().get_unitary(groverCircuit, decimals=3)

INIT = 0.5*np.array([[1,1,1,1],[1,-1,1,-1],[1,1,-1,-1],[1,-1,-1,1]])
CZ = np.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,-1]])
XX = np.array([[0,0,0,1],[0,0,1,0],[0,1,0,0],[1,0,0,0]])

'''Oracle'''
ORCL = np.dot(XX,np.dot(CZ,XX))
print(ORCL)

'''FUll circuit matrix so far'''
np.dot(ORCL,INIT)

groverCircuit.measure(qr,cr)
groverCircuit.draw(output="mpl")

simulator = Aer.get_backend('qasm_simulator')
result = execute(groverCircuit, simulator).result()
counts = result.get_counts(groverCircuit)
plot_histogram(counts, title='Grover Intermediate measurement')

qr = QuantumRegister(2)
cr = ClassicalRegister(2)
groverCircuit = QuantumCircuit(qr,cr)
groverCircuit.h(qr)

'''oracle w = |00>'''
groverCircuit.x(qr)
groverCircuit.cz(qr[0],qr[1])
groverCircuit.x(qr)

'''Grover Transform'''
groverCircuit.h(qr)
'''Reflection'''
groverCircuit.z(qr)
groverCircuit.cz(qr[0],qr[1])
'''end of Reflection'''
groverCircuit.h(qr)
'''end of Grover Transform'''



groverCircuit.draw(output="mpl")

job = execute(groverCircuit, backend)
job.result().get_unitary(groverCircuit, decimals=3)

HH = 0.5*np.array([[1,1,1,1],[1,-1,1,-1],[1,1,-1,-1],[1,-1,-1,1]])
ZZ = np.array([[1,0,0,0],[0,-1,0,0],[0,0,-1,0],[0,0,0,1]])

'''Grover Transform'''
GT =  np.dot(HH,np.dot(CZ,np.dot(ZZ,HH)))
print(GT)

'''FUll circut matrix'''
np.dot(INIT,np.dot(ORCL,GT))

simulator = Aer.get_backend('qasm_simulator')
result = execute(groverCircuit, simulator).result()
counts = result.get_counts(groverCircuit)
plot_histogram(counts, title='Grover Intermediate measurement')