import qiskit
qiskit.__qiskit_version__

#initialization
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np

# importing Qiskit
from qiskit import IBMQ, BasicAer
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, execute
from qiskit.compiler import transpile
from qiskit.tools.monitor import job_monitor

# import basic plot tools
from qiskit.tools.visualization import plot_histogram

# Load our saved IBMQ accounts.
IBMQ.load_account()

nQubits = 14 # number of physical qubits
a = 101      # the hidden integer whose bitstring is 1100101

# make sure that a can be represented with nQubits
a = a % 2**(nQubits)

# Creating registers
# qubits for querying the oracle and finding the hidden integer
qr = QuantumRegister(nQubits)
# for recording the measurement on qr
cr = ClassicalRegister(nQubits)

circuitName = "BernsteinVazirani"
bvCircuit = QuantumCircuit(qr, cr)

# Apply Hadamard gates before querying the oracle
for i in range(nQubits):
    bvCircuit.h(qr[i])
    
# Apply barrier so that it is not optimized by the compiler
bvCircuit.barrier()

# Apply the inner-product oracle
for i in range(nQubits):
    if (a & (1 << i)):
        bvCircuit.z(qr[i])
    else:
        bvCircuit.iden(qr[i])
        
# Apply barrier 
bvCircuit.barrier()

#Apply Hadamard gates after querying the oracle
for i in range(nQubits):
    bvCircuit.h(qr[i])
    
# Measurement
bvCircuit.barrier(qr)
bvCircuit.measure(qr, cr)

bvCircuit.draw(output='mpl')

# use local simulator
backend = BasicAer.get_backend('qasm_simulator')
shots = 1000
results = execute(bvCircuit, backend=backend, shots=shots).result()
answer = results.get_counts()
plot_histogram(answer)

backend = IBMQ.get_backend('ibmq_16_melbourne')
shots = 1000

bvCompiled = transpile(bvCircuit, backend=backend, optimization_level=1)
job_exp = execute(bvCircuit, backend=backend, shots=shots)    
job_monitor(job_exp)

results = job_exp.result()
answer = results.get_counts(bvCircuit)

threshold = int(0.01 * shots) #the threshold of plotting significant measurements
filteredAnswer = {k: v for k,v in answer.items() if v >= threshold} #filter the answer for better view of plots

removedCounts = np.sum([ v for k,v in answer.items() if v < threshold ]) #number of counts removed 
filteredAnswer['other_bitstrings'] = removedCounts  #the removed counts is assigned to a new index

plot_histogram(filteredAnswer)

print(filteredAnswer)