#from qiskit_textbook.widgets import bv_widget
#bv_widget(2, "11")

# initialization
# initialization

#######################################################################################################################   qiskit_edit_1
from qiskit import QuantumCircuit, execute
from qiskit import QuantumRegister, ClassicalRegister
from qiskit.tools.monitor import job_monitor
from qiskit import *
import time
import matplotlib.pyplot as plt
import argparse
import sys
from concurrent.futures import ThreadPoolExecutor
import resource
from time import sleep
#########################################################################################################################

import numpy as np

# importing Qiskit
#from qiskit import IBMQ, Aer
#from qiskit.providers.ibmq import least_busy
#from qiskit import QuantumCircuit, assemble, transpile

# import basic plot tools

from qiskit.visualization import plot_histogram

n = 3 # number of qubits used to represent s
s = '011'   # the hidden binary string

## noise options
options = {
    "plot": True,
    "thermal_factor": 0.99,
    "decoherence_factor": .99,
    "depolarization_factor": 0.99,
    "bell_depolarization_factor": 0.99,
    "decay_factor": 0.99,
    "rotation_error": {'rx':[.9, 0.], 'ry':[1., 0.], 'rz': [1., 0.]},
    "tsp_model_error": [1., 0.],
    "plot": False
}

# We need a circuit with n qubits, plus one auxiliary qubit
# Also need n classical bits to write the output to
bv_circuit = QuantumCircuit(n+1, n)

# put auxiliary in state |->
bv_circuit.h(n)
bv_circuit.z(n)

# Apply Hadamard gates before querying the oracle
for i in range(n):
    bv_circuit.h(i)
    
# Apply barrier 
bv_circuit.barrier()

# Apply the inner-product oracle
s = s[::-1] # reverse s to fit qiskit's qubit ordering
for q in range(n):
    if s[q] == '0':
        bv_circuit.i(q)
    else:
        bv_circuit.cx(q, n)
        
# Apply barrier 
bv_circuit.barrier()

#Apply Hadamard gates after querying the oracle
for i in range(n):
    bv_circuit.h(i)

# Measurement
for i in range(n):
    bv_circuit.measure(i, i, basis='Ensemble',add_param='Z')

bv_circuit.draw()

qc = QuantumCircuit(4,4)
s = '101'
string = '111'
for i in range(3):
    if string[i] == '1':
        qc.x(i)
qc.barrier()
s = s[::-1] # reverse s to fit qiskit's qubit ordering
for q in range(n):
    if s[q] == '0':
        qc.i(q)
    else:
        qc.cx(q, n)
# Measurement
for i in range(n):
    qc.measure(i, i, basis='Ensemble',add_param='Z')

qc.draw()

# use local simulator
backend = qiskit.BasicAer.get_backend('dm_simulator')

# without noise
job = execute(qc, backend=backend, shots=1)
job_result = job.result()
#print(job_result['results'][0]['data']['densitymatrix'])
job_result['results'][0]['data']['ensemble_probability']

# use local simulator
backend = qiskit.BasicAer.get_backend('dm_simulator')

# without noise
job = execute(bv_circuit, backend=backend, shots=1)
job_result = job.result()
#print(job_result['results'][0]['data']['densitymatrix'])
job_result['results'][0]['data']['ensemble_probability']

#with noise
job1 = execute(bv_circuit, backend=backend, backend_options=options, shots=1)
job_result1 = job1.result()
#print(job_result1['results'][0]['data']['densitymatrix'])
job_result1['results'][0]['data']['ensemble_probability']

no_noise=job_result['results'][0]['data']['ensemble_probability']

noisy=job_result1['results'][0]['data']['ensemble_probability']
labels = noisy.keys()
without_noise = no_noise.values()
with_noise = noisy.values()

x = np.arange(len(labels))  # the label locations
width = 0.35  # the width of the bars

fig, ax = plt.subplots()
rects1 = ax.bar(x - width/2, without_noise, width, label='Without Noise')
rects2 = ax.bar(x + width/2, with_noise, width, label='With Noise')

# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Probability')
ax.set_title('Ensemble Probabilities with Noise')
ax.set_xticks(x)
ax.set_xticklabels(labels)
ax.legend()
plt.show()

# Load our saved IBMQ accounts and get the least busy backend device with less than or equal to 5 qubits
IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q')
provider.backends()
backend = least_busy(provider.backends(filters=lambda x: x.configuration().n_qubits <= 5 and
                                   x.configuration().n_qubits >= 2 and
                                   not x.configuration().simulator and x.status().operational==True))
print("least busy backend: ", backend)

# Run our circuit on the least busy backend. Monitor the execution of the job in the queue
from qiskit.tools.monitor import job_monitor

shots = 1024
transpiled_bv_circuit = transpile(bv_circuit, backend)
job = backend.run(transpiled_bv_circuit, shots=shots)

job_monitor(job, interval=2)

# Get the results from the computation
results = job.result()
answer = results.get_counts()

plot_histogram(answer)

from qiskit_textbook.widgets import bv_widget
bv_widget(3, "011", hide_oracle=False)

import qiskit.tools.jupyter
%qiskit_version_table