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
from scipy import linalg as la

from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, QISKitError
from qiskit import available_backends, execute, register, get_backend

# import state tomography functions
from qiskit.tools.visualization import plot_histogram, plot_state

def ghz_state(q, c, n):
    # Create a GHZ state
    qc = QuantumCircuit(q, c)
    qc.h(q[0])
    for i in range(n-1):
        qc.cx(q[i], q[i+1])
    return qc

def superposition_state(q, c):
    # Create a Superposition state
    qc = QuantumCircuit(q, c)
    qc.h(q)
    return qc

# Build the quantum cirucit. We are going to build two circuits a GHZ over 3 qubits and a
# superpositon over all 3 qubits 

n = 3  # number of qubits 
q = QuantumRegister(n)
c = ClassicalRegister(n)

# quantum circuit to make a GHZ state 
ghz = ghz_state(q, c, n)


# quantum circuit to make a superposition state 
superposition = superposition_state(q, c)

measure_circuit = QuantumCircuit(q,c)
measure_circuit.measure(q, c)

# execute the quantum circuit 
backend = 'local_qasm_simulator' # the device to run on
circuits = [ghz+measure_circuit, superposition+measure_circuit]
job = execute(circuits, backend=backend, shots=1000)

plot_histogram(job.result().get_counts(circuits[0]))
plot_histogram(job.result().get_counts(circuits[1]),15)

n = 2  # number of qubits 
q = QuantumRegister(n)
c = ClassicalRegister(n)

qc = QuantumCircuit(q, c)
qc.h(q[1])


# execute the quantum circuit 
job = execute(qc, backend='local_statevector_simulator')
state_superposition = job.result().get_statevector(qc)
state_superposition

def overlap(state1, state2):
    return round(np.dot(state1.conj(), state2))

print(state_superposition)

overlap(state_superposition, state_superposition)

def expectation_value(state, Operator):
    return round(np.dot(state.conj(), np.dot(Operator, state)).real)

X = np.array([[0, 1], [1, 0]])
Z = np.array([[1, 0], [0, -1]])
IZ = np.kron(np.eye(2), Z)
ZI = np.kron(Z, np.eye(2))
IX = np.kron(np.eye(2), X)
XI = np.kron(X, np.eye(2))


print("Operator Z on qubit 0 is " + str(expectation_value(state_superposition, IZ)))
print("Operator Z on qubit 1 is " + str(expectation_value(state_superposition, ZI)))
print("Operator X on qubit 0 is " + str(expectation_value(state_superposition, IX)))
print("Operator X on qubit 1 is " + str(expectation_value(state_superposition, XI)))

def state_2_rho(state):
    return np.outer(state, state.conj())

rho_superposition=state_2_rho(state_superposition)
print(rho_superposition)

plot_state(rho_superposition,'city')

plot_state(rho_superposition,'paulivec')

plot_state(rho_superposition,'qsphere')

plot_state(rho_superposition,'bloch')

n = 2  # number of qubits 
q = QuantumRegister(n)
c = ClassicalRegister(n)

qc2 = QuantumCircuit(q, c)
qc2.h(q[1])
qc2.z(q[1])


# execute the quantum circuit 
job = execute(qc2, backend='local_statevector_simulator')
state_neg_superposition = job.result().get_statevector(qc2)
rho_neg_superposition=state_2_rho(state_neg_superposition)
plot_state(rho_neg_superposition, 'qsphere')

plot_state(0.5*rho_neg_superposition + 0.5* rho_superposition, 'qsphere')

