# Welcome to qiskit
import qiskit as qk

myvers = qk.__version__
print("You are using qiskit version", myvers)
print("More details on qiskit components:",qk.__qiskit_version__)

my_token = ''
# Set up your credentials for the IBM Quantum Experience in one-time mode (easiest)
ibmq_provider = qk.IBMQ.enable_account(my_token)

# Check the available local backends

qk.Aer.backends()

# Check the available cloud-based backends

ibmq_provider.backends()

qr = qk.QuantumRegister(2,name='qreg')
cr = qk.ClassicalRegister(2,name='creg')
qc = qk.QuantumCircuit(qr,cr,name='test_circ')  # A QuantumCircuit is composed of quantum and classical registers

qc.x(qr[0])

for indx in range(2):
    qc.h(qr[indx])

qc.cx(qr[0],qr[1]) # The first entry is the control qubit, the second entry is the target qubit for the CNOT

qc.measure(qr[0],cr[0])
qc.measure(qr[1],cr[1])

qc.draw()

qc.draw(output='mpl')

backend1 = qk.Aer.get_backend('qasm_simulator')

job1 = qk.execute(qc, backend1, shots=1024) # The shots are the number of times the circuit will be run.
                                            # More shots will mean better reconstruction of the measurement
                                            # statistics, but also longer execution times

result1 = job1.result()
counts1 = result1.get_counts(qc)
print(counts1)

from qiskit.tools.visualization import plot_histogram
plot_histogram(counts1)

backend2 = ibmq_provider.get_backend('ibmqx2')

job2 = qk.execute(qc, backend=backend2, shots=1024)

from qiskit.tools.monitor import job_monitor
job_monitor(job2, interval=5)

result2 = job2.result()
counts2 = result2.get_counts(qc)
print(counts2)
plot_histogram(counts2)

jobID = job2.job_id()
print(jobID)

job_get = backend2.retrieve_job(jobID)
counts_get = job_get.result().get_counts()
print(counts_get)

plot_histogram([counts1,counts_get])

from qiskit.tools.visualization import plot_gate_map

print('List of pairwise connection between qubits available for CNOT operations:\n',
      backend2.configuration().coupling_map)

plot_gate_map(backend2)

custom_layout = [3,4] # abstract qr[0] will be mapped in q[3] on the device
                      # abstract qr[1] will be mapped in q[4] on the device

compiled_qc = qk.transpile(qc,backend = backend2,initial_layout = custom_layout,optimization_level=0)
compiled_qc.draw()

from qiskit.tools.visualization import plot_circuit_layout

plot_circuit_layout(compiled_qc,backend2)

compiled_qobj = qk.assemble(compiled_qc,shots=1024)
job3 = backend2.run(compiled_qobj)

job4 = qk.execute(qc,backend=backend2,shots=1024,initial_layout=custom_layout,optimization_level=0)

custom_layout_bad = [1,3] # There is no 1 -> 3 connection on the device
compiled_qc_bad = qk.transpile(qc, backend = backend2, initial_layout = custom_layout_bad,optimization_level=0)

compiled_qc_bad.draw(output='mpl')

plot_circuit_layout(compiled_qc_bad,backend2)

qr = qk.QuantumRegister(5,name='qreg')
cr = qk.ClassicalRegister(5,name='creg')
qc = qk.QuantumCircuit(qr,cr,name='exercise')

qc.h(qr[0])
for i in range(1,5):
    qc.cx(qr[0],qr[i])

qc.draw(output='mpl')