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


def qft_rotations(circuit, n):
    """Performs qft on the first n qubits in circuit (without swaps)"""
    if n == 0:
        return circuit
    n -= 1
    circuit.h(n)
    for qubit in range(n):
        circuit.cu1(np.pi/2**(n-qubit), qubit, n)
    # At the end of our function, we call the same function again on
    # the next qubits (we reduced n by one earlier in the function)
    qft_rotations(circuit, n)
def swap_registers(circuit, n):
    for qubit in range(n//2):
        circuit.swap(qubit, n-qubit-1)
    return circuit

def qft(circuit, n):
    """QFT on the first n qubits in circuit"""
    qft_rotations(circuit, n)
    swap_registers(circuit, n)
    return circuit

# Let's see how it looks:
qc = QuantumCircuit(4,4)
qft(qc,4)
qc.draw(output = 'mpl')

def inverse_qft(circuit, n):
    """Does the inverse QFT on the first n qubits in circuit"""
    # First we create a QFT circuit of the correct size:
    qft_circ = qft(QuantumCircuit(n), n)
    # Then we take the inverse of this circuit
    invqft_circ = qft_circ.inverse()
    # And add it to the first n qubits in our existing circuit
    circuit.append(invqft_circ, circuit.qubits[:n])
    return circuit.decompose() # .decompose() allows us to see the individual gates

qc = inverse_qft(qc,4)

qc.measure(range(4),range(4))

def run_circuit(qc):
    backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend
    result = execute(qc, backend, shots = 10000).result() # we run the simulation
    counts = result.get_counts() # we get the counts
    return counts

counts = run_circuit(qc)
print(counts)
plot_histogram(counts)
# we should get the intial state '0000'

qc1 = QuantumCircuit(3,3)
qc1.x(0)
qc1.x(1)
qc1.x(2) # try for different initial values 

for i in range(4):# choose the number of times you want to do qft)
    qft(qc1,3)

qc1.measure(0,0)   
qc1.measure(1,1)
qc1.measure(2,2)
def run_circuit(qc1):
    backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend
    result = execute(qc1, backend, shots = 10000).result() # we run the simulation
    counts = result.get_counts() # we get the counts
    return counts

counts = run_circuit(qc1)
print(counts)
plot_histogram(counts)

qc2 = QuantumCircuit(2)

for i in range(2):
    qft(qc2, 2)
    
matrix = qi.Operator(qc2).data
print(matrix)

def Modulo_increment(qc):
    qft(qc,4)
    for i in range(4):
        qc.u1(np.pi/(2**(3-i)), i)
    qft(qc, 4)
    return qc

# checking for the case of '1000'
qc2 = QuantumCircuit(4,4)
qc2.x(3)
qc2 = Modulo_increment(qc2)
qc2.measure(range(4),range(4))
# qc2.draw('mpl')
def run_circuit(qc2):
    backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend
    result = execute(qc2, backend, shots = 10000).result() # we run the simulation
    counts = result.get_counts() # we get the counts
    return counts

counts = run_circuit(qc2)
print(counts)
plot_histogram(counts)

# checking for the case of '1101'
qc3 = QuantumCircuit(4,4)
qc3.x(3)
qc3.x(2)
qc3.x(0)
qc3 = Modulo_increment(qc2)
qc3.measure(range(4),range(4))
def run_circuit(qc3):
    backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend
    result = execute(qc3, backend, shots = 10000).result() # we run the simulation
    counts = result.get_counts() # we get the counts
    return counts

counts = run_circuit(qc3)
print(counts)
plot_histogram(counts)

# checking for the case of '1111'
qc4 = QuantumCircuit(4,4)
qc4.x(range(4))
qc4 = Modulo_increment(qc4)
qc4.measure(range(4),range(4))
def run_circuit(qc4):
    backend = Aer.get_backend('qasm_simulator') # we choose the simulator as our backend
    result = execute(qc4, backend, shots = 10000).result() # we run the simulation
    counts = result.get_counts() # we get the counts
    return counts

counts = run_circuit(qc4)
print(counts)
plot_histogram(counts)

