import sys, getpass
try:
    sys.path.append("../../") # go to parent dir
    import Qconfig
    qx_config = {
        "APItoken": Qconfig.APItoken,
        "url": Qconfig.config['url']}
    print('Qconfig loaded from %s.' % Qconfig.__file__)
except:
    APItoken = getpass.getpass('Please input your token and hit enter: ')
    qx_config = {
        "APItoken": APItoken,
        "url":"https://quantumexperience.ng.bluemix.net/api"}
    print('Qconfig.py not found in qiskit-tutorial directory; Qconfig loaded using user input.')

# useful additional packages 
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
from pprint import pprint

# importing QISKit
from qiskit import QuantumProgram, QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit import available_backends, execute, register, least_busy

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

backend = 'local_qasm_simulator' # run on local simulator by default

# Uncomment the following lines to run on a real device
# register(qx_config['APItoken'], qx_config['url'])
# backend = least_busy(available_backends({'simulator': False, 'local': False}))
# print("the best backend is " + backend)

zero = np.array([[1],[0]])
one = np.array([[0],[1]])

X = np.array([[0,1],[1,0]])
print(np.dot(X,zero))
print(np.dot(X,one))

# Creating registers
qr = QuantumRegister(1)
cr = ClassicalRegister(1)

# Quantum circuit ground 
qc_ground = QuantumCircuit(qr, cr)
qc_ground.measure(qr[0], cr[0])

# Quantum circuit excited 
qc_excited = QuantumCircuit(qr, cr)
qc_excited.x(qr)
qc_excited.measure(qr[0], cr[0])

circuit_drawer(qc_ground)

circuit_drawer(qc_excited)

circuits = [qc_ground, qc_excited]
job = execute(circuits, backend)
result = job.result()

plot_histogram(result.get_counts(qc_ground))

plot_histogram(result.get_counts(qc_excited))

# Quantum circuit superposition 
qc_superposition = QuantumCircuit(qr, cr)
qc_superposition.h(qr)
qc_superposition.measure(qr[0], cr[0])

circuit_drawer(qc_superposition)

job = execute(qc_superposition, backend)
result = job.result()

plot_histogram(result.get_counts(qc_superposition))

# Quantum circuit two Hadamards 
qc_twohadamard = QuantumCircuit(qr, cr)
qc_twohadamard.h(qr)
qc_twohadamard.barrier()
qc_twohadamard.h(qr)
qc_twohadamard.measure(qr[0], cr[0])

circuit_drawer(qc_twohadamard)

job = execute(qc_twohadamard, backend)
result = job.result()

plot_histogram(result.get_counts(qc_twohadamard))

state_overlap = lambda state1, state2: np.absolute(np.dot(state1.conj().T,state2))**2

Hadamard = np.array([[1,1],[1,-1]],dtype=complex)/np.sqrt(2)
psi1 = np.dot(Hadamard,zero)
P0 = state_overlap(zero,psi1)
P1 = state_overlap(one,psi1)
plot_histogram({'0' : P0.item(0), '1' : P1.item(0)})

print(np.dot(Hadamard,zero))
print(np.dot(Hadamard,one))

# Creating registers
q2 = QuantumRegister(2)
c2 = ClassicalRegister(2)

# quantum circuit to make an entangled bell state 
bell = QuantumCircuit(q2, c2)
bell.h(q2[0])
bell.cx(q2[0], q2[1])

# quantum circuit to measure q0 in the standard basis
measureIZ = QuantumCircuit(q2, c2)
measureIZ.measure(q2[0], c2[0])
bellIZ = bell+measureIZ

# quantum circuit to measure q0 in the superposition basis 
measureIX = QuantumCircuit(q2, c2)
measureIX.h(q2[0])
measureIX.measure(q2[0], c2[0])
bellIX = bell+measureIX

# quantum circuit to measure q1 in the standard basis
measureZI = QuantumCircuit(q2, c2)
measureZI.measure(q2[1], c2[1])
bellZI = bell+measureZI

# quantum circuit to measure q1 in the superposition basis 
measureXI = QuantumCircuit(q2, c2)
measureXI.h(q2[1])
measureXI.measure(q2[1], c2[1])
bellXI = bell+measureXI

# quantum circuit to measure q in the standard basis 
measureZZ = QuantumCircuit(q2, c2)
measureZZ.measure(q2[0], c2[0])
measureZZ.measure(q2[1], c2[1])
bellZZ = bell+measureZZ

# quantum circuit to measure q in the superposition basis 
measureXX = QuantumCircuit(q2, c2)
measureXX.h(q2[0])
measureXX.h(q2[1])
measureXX.measure(q2[0], c2[0])
measureXX.measure(q2[1], c2[1])
bellXX = bell+measureXX

circuit_drawer(bellIZ)

circuit_drawer(bellIX)

circuit_drawer(bellZI)

circuit_drawer(bellXI)

circuit_drawer(bellZZ)

circuit_drawer(bellXX)

circuits = [bellIZ,bellIX,bellZI,bellXI,bellZZ,bellXX]
job = execute(circuits, backend)
result = job.result()

plot_histogram(result.get_counts(bellIZ))

result.get_data(bellIZ)

plot_histogram(result.get_counts(bellIX))

plot_histogram(result.get_counts(bellZI))
plot_histogram(result.get_counts(bellXI))

plot_histogram(result.get_counts(bellZZ))

plot_histogram(result.get_counts(bellXX))

# quantum circuit to make a mixed state 
mixed1 = QuantumCircuit(q2, c2)
mixed2 = QuantumCircuit(q2, c2)
mixed2.x(q2)
mixed1.measure(q2[0], c2[0])
mixed1.measure(q2[1], c2[1])
mixed2.measure(q2[0], c2[0])
mixed2.measure(q2[1], c2[1])

circuit_drawer(mixed1)

circuit_drawer(mixed2)

mixed_state = [mixed1,mixed2]
job = execute(mixed_state, backend)
result = job.result()

counts1 = result.get_counts(mixed_state[0])
counts2 = result.get_counts(mixed_state[1])

from collections import Counter
ground = Counter(counts1)
excited = Counter(counts2)
plot_histogram(ground+excited)

