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()

import numpy as np
from numpy import pi
# importing Qiskit
from qiskit import QuantumCircuit, transpile, assemble, Aer, IBMQ
from qiskit.providers.ibmq import least_busy
from qiskit.tools.monitor import job_monitor
from qiskit.visualization import plot_histogram, plot_bloch_multivector

qc = QuantumCircuit(10)
qc.h(2)
qc.cp(pi/2, 1, 2)
qc.cp(pi/4, 0, 2)
qc.h(1)
qc.cp(pi/2, 0, 1) # CROT from qubit 0 to qubit 1
qc.h(0)
qc.swap(0,2)

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.cp(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)

# Let's see how it looks:
qc = QuantumCircuit(10)
qft_rotations(qc,10)
qc.draw()


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(10)
qft(qc,10)
qc.draw()

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
nqubits = 10
number = 5
qc = QuantumCircuit(nqubits)
for qubit in range(nqubits):
    qc.h(qubit)
qc.p(number*pi/4,0)
qc.p(number*pi/2,1)
qc.p(number*pi,2)
qc.p(number*pi/4,3)
qc.p(number*pi/2,4)
qc.p(number*pi,5)
qc.p(number*pi/4,6)
qc.p(number*pi/2,7)
qc.p(number*pi,8)
qc.p(number*pi/4,9)

qc.draw()

위에 코드들은 ibm qiskit textbook을 기초로 하여서 작성한 코드이다.
10큐비트로 바뀌었으므로 nqubits = 10) <- 10을 작성하여서 10큐비트를 구현하도록 하였고 위 그림은 그에 따른 결과이다.
그리고 원래는 3큐비트만 하여서 코드들이 구현이 되도록 하였지만 문제를 풀기위해서는 10큐비트를 작성하고 코드연산들을 적용해야한다.
그러므로 h 게이트도 10큐비트까지 다 적용되도록 하였고 p 게이트가 도는 각도들도 일정패턴을 가지고 적용되도록 작성하였다.
qc.p(number*pi/4,n)
qc.p(number*pi/2,n)
qc.p(number*pi,n)
=> 의 파이 각도들이 일정하게 반복되도록 코드들을 추가하였다.

qc_init = qc.copy()
qc_init.save_statevector()
sim = Aer.get_backend("aer_simulator")
statevector = sim.run(qc_init).result().get_statevector()
plot_bloch_multivector(statevector)

qc = inverse_qft(qc, nqubits)
qc.measure_all()
qc.draw()

shots = 2048
backend = Aer.get_backend('qasm_simulator')
transpiled_qc = transpile(qc, backend, optimization_level=3)
job = backend.run(transpiled_qc, shots=shots)
job_monitor(job)

counts = job.result().get_counts()
plot_histogram(counts)

print(counts)

