import math
import numpy as np

import matplotlib as mpl
from matplotlib import pyplot as plt 

import qiskit
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit import Aer, execute
from qiskit.visualization import plot_bloch_multivector
from qiskit.visualization import plot_bloch_vector
from qiskit.visualization import plot_histogram

qbackend = Aer.get_backend('qasm_simulator')
sbackend = Aer.get_backend('statevector_simulator')
ubackend = Aer.get_backend('unitary_simulator')
qiskit.__version__

qreg = QuantumRegister(1)
creg = ClassicalRegister(1)

circ = QuantumCircuit(qreg,creg)
circ.measure(0,0)

circ.draw(output='mpl')

job = execute( circ, qbackend, shots=1000 )
counts = job.result().get_counts()

fig, ax = plt.subplots()
plot_histogram(counts, ax = ax)

n_shots=1000

measure_psi = QuantumCircuit(1,1)
amps_psi = (1.0/np.sqrt(2))*np.array([1.0+0.0j, 0.0+1.0j])
measure_psi.initialize(amps_psi, 0)
measure_psi.measure(0,0)

job_psi = execute( measure_psi, qbackend, shots=n_shots )
counts_psi = job_psi.result().get_counts()
print("counts_psi =", counts_psi)

measure_phi = QuantumCircuit(1,1)
amps_phi = (1.0/np.sqrt(2))*np.array([0.0+1.0j, -1.0+0.0j])
measure_phi.initialize(amps_phi, 0)
measure_phi.measure(0,0)

job_phi = execute( measure_phi, qbackend, shots=n_shots )
counts_phi = job_phi.result().get_counts()
print("counts_phi =", counts_phi)

fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(12,5), dpi=96)
plot_histogram(counts_psi, ax=axes[0])
axes[0].set_title(r'$\psi$')
plot_histogram(counts_phi, ax=axes[1])
axes[1].set_title(r'$\phi$');

n_shots=10000

measure_psi = QuantumCircuit(1,1)
amps_psi = [np.sqrt(0.45), np.sqrt(0.55)]
measure_psi.initialize(amps_psi, 0)
measure_psi.measure(0,0)

job_psi = execute( measure_psi, qbackend, shots=n_shots )
counts_psi = job_psi.result().get_counts()
print("counts_psi =", counts_psi)

measure_phi = QuantumCircuit(1,1)
amps_phi = [np.sqrt(0.5), np.sqrt(0.5)]
measure_phi.initialize(amps_phi, 0)
measure_phi.measure(0,0)

job_phi = execute( measure_phi, qbackend, shots=n_shots )
counts_phi = job_phi.result().get_counts()
print("counts_phi =", counts_phi)

fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(12,5), dpi=96)
plot_histogram(counts_psi, ax=axes[0])
axes[0].set_title(r'$\psi$')
plot_histogram(counts_phi, ax=axes[1])
axes[1].set_title(r'$\phi$');

qreg = QuantumRegister(1)
creg = ClassicalRegister(1)

circ = QuantumCircuit(qreg,creg)

# Apply gates
circ.x(0)

# Measure
circ.barrier(qreg)
circ.measure(0,0)

circ.draw(output='mpl')

job = execute( circ, qbackend, shots=10000 )
state = job.result().get_counts()
print("Counts:", state)
plot_histogram(state)

qreg = QuantumRegister(1)
creg = ClassicalRegister(1)

circ = QuantumCircuit(qreg,creg)

# Apply gates
circ.h(0)

# Measure
circ.barrier(qreg)
circ.measure(0,0)

circ.draw(output='mpl')

job = execute( circ, qbackend, shots=10000 )
state = job.result().get_counts()
print("Counts:", state)
plot_histogram(state)

qreg = QuantumRegister(1)
creg = ClassicalRegister(1)

circ = QuantumCircuit(qreg,creg)

# Apply gates
circ.z(0)
circ.x(0)
circ.h(0)

# Measure
circ.barrier(qreg)
circ.measure(0,0)

circ.draw(output='mpl')

job = execute( circ, qbackend, shots=10000 )
state = job.result().get_counts()
print("Counts:", state)
plot_histogram(state)

ps = np.array([0.1, 0.2, .5, .2])
amplitudes = np.sqrt(ps)

qreg = QuantumRegister(2)
creg = ClassicalRegister(2)

circ = QuantumCircuit(qreg,creg)

circ.initialize(amplitudes, qreg)
circ.measure(qreg,creg)

circ.draw(output='mpl')

job = execute( circ, qbackend, shots=10000 )
state = job.result().get_counts()
print("Counts:", state)
plot_histogram(state)

# Random 4-qubit state
ps = np.random.randint(0, 255, 16)
ps = ps/ps.sum()
amplitudes = np.sqrt(ps)

# Setup
qreg = QuantumRegister(4)
creg = ClassicalRegister(4)
circ = QuantumCircuit(qreg,creg)
circ.initialize(amplitudes, qreg)

# Measure
m = QuantumCircuit(qreg, creg)
m.measure(qreg, creg)

job = execute( circ+m, qbackend, shots=10000 )
counts = job.result().get_counts()

m.draw(output='mpl')

plot_histogram(counts)

qreg = QuantumRegister(2)
creg = ClassicalRegister(2)

circ = QuantumCircuit(qreg,creg)

circ.x(0)
circ.h(0)
circ.cx(0,1)

circ.barrier(qreg)
circ.measure(qreg,creg)

circ.draw(output='mpl')

job = execute( circ, qbackend, shots=10000 )
counts = job.result().get_counts()
plot_histogram(counts)

# Create the quantum circuit with 2 qubits and 2 classical bits
qc = QuantumCircuit(2, 2)

qc.barrier()


# First generate the entangled pair between Alice and Bob (Remember: Hadamard followed by CX generates a Bell pair)
# At this point, qubit 1 goes to Bob and qubit 2 goes to Alice
qc.h(0)
qc.cx(0, 1)
qc.barrier()

# Next, Alice encodes her message. First, we need to prepare the state Alice will like to send,
# In this tutorial, Alice is sending 11:

qc.x(0)
qc.z(0)
qc.barrier()

# Alice then sends her qubit to Bob.
# After recieving qubit 0, Bob applies the recovery protocol:
qc.cx(0, 1)
qc.h(0)

# Let's take a measurement to see which classical bit of information Bob receives:
qc.barrier()
qc.measure(0, 0)
qc.measure(1, 1)

# Draw our output
qc.draw(output = "mpl")

job = execute( qc, qbackend, shots=10000 )
counts = job.result().get_counts()
plot_histogram(counts)

q1 = QuantumCircuit(1,1)
q1.x(0)
q1.measure(0,0)
q1.draw(output = 'mpl')

q2 = QuantumCircuit(1,1)
q2.h(0)
q2.measure(0,0)
q2.draw(output = 'mpl')

q = q1 + q2
q.draw(output = 'mpl')

