# Setup básico
!pip install qiskit -q
!pip install qiskit[visualization] -q
import qiskit as qk

!pip install qiskit-aer -q
import qiskit_aer as qk_aer

import numpy as np
np.set_printoptions(precision=3, suppress=True)

from matplotlib import pyplot as plt
%matplotlib inline

import pandas as pd
import sklearn as sk

# Remember that qiskit has to be already installed in the Python environment.
# Otherwise the import command will fail
import qiskit as qk

print("A Qubit (initialized in the state |0> plus one classical bit")
qr = qk.QuantumRegister(1,'q')
cr = qk.ClassicalRegister(1,'c')
qc = qk.QuantumCircuit(qr, cr)
display(qc.draw('mpl', style = 'clifford', scale = 1))

print("A qubit forced initialized in the state |0>")
qc.initialize([1,0])
display(qc.draw('mpl', style = 'clifford', scale = 1))

print("A qubit forced initialized in |0> and a measurement")
qc.measure([0],[0])
display(qc.draw('mpl', style = 'clifford'))

import qiskit as qk
import qiskit_aer as qk_aer

# A list of possible measurements.
qk_aer.Aer.backends()[7:]

import qiskit as qk

# To simplify the code let's use the function QuantumCircuit(n,m)
#  which creates n Qubits and m Classical Registers at once
qc = qk.QuantumCircuit(1,1)

# Also, it is not necessary to force initialize the qubits. Whenever
#  they are created, they start from |0>.
qc.measure([0],[0])
display(qc.draw('mpl', style = 'clifford', scale=1))

# Let's choose a Aer backend with the aer_simulator
backend = qk_aer.Aer.get_backend('aer_simulator')

# Next we "transpile" the circuit through the backend chosen
qc_exe = qk.transpile(qc, backend)

# And execute the circuit qc in the simulator backend
#  getting as final result the counts from 1.000 measures
#  of the qubit state
result = backend.run(qc_exe, shots=1000).result().get_counts()
result

import qiskit as qk
import qiskit_aer as qk_aer

backend = qk_aer.Aer.get_backend('aer_simulator')
qc_exe = qk.transpile(qc, backend)
result = backend.run(qc, shots=1000).result().get_counts()
qk.visualization.plot_distribution(result)

def show_histogram(qc,shots=1000):
  display(qc.draw('mpl', style = 'clifford', scale=1))
  backend = qk_aer.Aer.get_backend('aer_simulator')
  qc_exe = qk.transpile(qc, backend)
  result = backend.run(qc, shots=1000).result().get_counts()
  print(result)
  grafico = qk.visualization.plot_distribution(result)
  display(grafico)
  return([result, grafico])

show_histogram(qc);

import numpy as np
v0 = np.array([[1],[0]]);v0

v1 = np.array([[0],[1]]); v1

X = np.array([[0,1],[1,0]]); X

X.dot(v0)

X.dot(v1)

import qiskit as qk
# Notice the compact way of creating a circuit. We indicate just the number of
#  qubits and in the end we make a measurement of all qubits with measure_all()
# Also, when using measure_all(), Qiskit inserts a barrier between the Qubits
#  and the measurements (classical bits)
qc = qk.QuantumCircuit(1)
qc.x(0)
qc.measure_all()
show_histogram(qc);

import numpy as np
# Notice that we are creating the v0 matrix using the transpose operation
v0 = np.array([[1,0]]).T; v0

# Here it is created again de X matrix
X = np.array([[0,1],[1,0]]); X

# Multiplying v0 by the X matrix twice you get again v0
X.dot(X).dot(v0)

# Multiplying the X matrix by itself you get the Identity matrix
X.dot(X)

import qiskit as qk
qc = qk.QuantumCircuit(1)
qc.x(0)
qc.x(0)
qc.measure_all()
show_histogram(qc);

import qiskit as qk

qc = qk.QuantumCircuit(1)
qc.initialize([2**-0.5,2**-0.5],0)
qc.measure_all()
show_histogram(qc);

import numpy as np
v0 = np.array([[1,0]]).T; v0

H = np.array([[1,1],[1,-1]])/np.sqrt(2); H

H.dot(v0)

import qiskit as qk

qc = qk.QuantumCircuit(1)
qc.h(0)
qc.measure_all()
show_histogram(qc);

import qiskit as qk

qc = qk.QuantumCircuit(1)
qc.initialize([2**-0.5,-(2**-0.5)],0)
qc.measure_all()
show_histogram(qc)

import qiskit as qk
qc = qk.QuantumCircuit(1)
qc.initialize([2**-0.5,-(2**-0.5)],0)
qc.h(0)
qc.measure_all()
show_histogram(qc);

import qiskit as qk
qc = qk.QuantumCircuit(1)
qc.x(0)
qc.h(0)
qc.h(0)
qc.measure_all()
show_histogram(qc);

import numpy as np

# First let's start with the qubit in the state |psi> = (|0> - |1>)/sqrt(2)
psi = np.array([[1,-1]]).T/(2**0.5); psi

H = np.array([[1,1],[1,-1]])/2**0.5; H

# Now let's pass the qubit Psi through an Hadamard gate.
# The result is a qubit in the state |1>
H.dot(psi)

# Let's start with a qubit in the state |1>, pass it through a
#  a hadamard gate twice and check the result
v0 = np.array([[0,1]]).T; v0

H.dot(H).dot(v0)

# This means that if we multiply the H gate by itself the result
#  will be an Identity matrix. Let's check it.
H.dot(H)

import qiskit as qk
qc = qk.QuantumCircuit(2)
qc.measure_all()
show_histogram(qc);

import numpy as np
psi1 = np.array([[1,0]]).T; psi1

psi2 = np.array([[1,0]]).T; psi2

# In numpy the tensor product is calculated with the function kron
np.kron(psi1,psi2)

import qiskit as qk
qc = qk.QuantumCircuit(2)
qc.h(0)
qc.h(1)
qc.measure_all()
show_histogram(qc);

import numpy as np
psi1 = np.array([[1,0]]).T;psi1

psi2 = np.array([[1,0]]).T;psi2

H = np.array([[1,1],[1,-1]])/2**0.5;H

# When we want to combine two vector states or gate matrices we tensor product them.
psi3 = np.kron(psi1,psi2);psi3

H2 = np.kron(H,H);H2

# When we want to pass a vetor through a gate we calculate the dot product
#  of the total gate matrix with the total vector.
# As we have predicted, the resulting vector state has a=b=c=d=1/2
psi4 = H2.dot(psi3); psi4

import qiskit as qk
qc = qk.QuantumCircuit(2)
qc.x([0,1])
qc.h(0)
qc.h(1)
qc.measure_all()
show_histogram(qc);

import numpy as np
psi1 = np.array([[0,1]]).T;psi1

psi2 = np.array([[0,1]]).T;psi2

H = np.array([[1,1],[1,-1]])/2**0.5;H

# When we want to combine two vector states or gate matrices we tensor product them.
psi3 = np.kron(psi1,psi2);psi3

H2 = np.kron(H,H);H2

# When we want to pass a vetor through a gate we calculate the dot product
#  of the total gate matrix with the total vector.
# As we have predicted, the resulting vector state has a=b=c=d=1/2
psi4 = H2.dot(psi3); psi4

import qiskit as qk
qc = qk.QuantumCircuit(2)
qc.cx(0,1)
qc.measure_all()
show_histogram(qc);

import numpy as np
C = np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]); C

v00 = np.array([[1,0,0,0]]).T;v00

# C.v00 = v00
C.dot(v00)

# Please notice that Qiskit's qubits presentation order is reversed.
#  Therefore 10 in the histogram's x axis should be read as 01 (from
#  inside out or right to left). This makes sense when pairing a
#  a circuit with an histogram

import qiskit as qk
qc = qk.QuantumCircuit(2)
qc.x(1)
qc.cx(0,1)
qc.measure_all()
show_histogram(qc)

import numpy as np
C = np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]); C

v01 = np.array([[0,1,0,0]]).T;v01

# C.v01 = v01
C.dot(v01)

import qiskit as qk
qc = qk.QuantumCircuit(2)
qc.x(1)
qc.cx(0,1)
qc.measure_all()
show_histogram(qc);

import numpy as np
C = np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]); C

v10 = np.array([[0,0,1,0]]).T; v10

# C.v10 = v11
C.dot(v10)

# Again remember to read qiskit qubits state presentation order
#  from right to left. Therefore 01 in the x axis is in fact 10

import qiskit as qk
qc = qk.QuantumCircuit(2)
qc.x([0,1])
qc.cx(0,1)
qc.measure_all()
show_histogram(qc);

import numpy as np
C = np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]); C

v11 = np.array([[0,0,0,1]]).T; v11

# C.v11 = v10
C.dot(v11)

import qiskit as qk
qc = qk.QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)
qc.measure_all()
show_histogram(qc);

import numpy as np
va = np.array([[1,0]]).T; va

vb = np.array([[1,0]]).T; vb

H = np.array([[1,1],[1,-1]])/2**0.5;H

vaH = H.dot(va); vaH

vaHvb = np.kron(vaH,vb); vaHvb

C = np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]); C

vout = C.dot(vaHvb); vout

import qiskit as qk
qc = qk.QuantumCircuit(2)
qc.x(0)
qc.h(0)
qc.cx(0,1)
qc.measure_all()
show_histogram(qc);

import numpy as np
va = np.array([[0,1]]).T; va

vb = np.array([[1,0]]).T; vb

H = np.array([[1,1],[1,-1]])/2**0.5;H

vaH = H.dot(va); vaH

vaHvb = np.kron(vaH,vb); vaHvb

C = np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]); C

vout = C.dot(vaHvb); vout

import qiskit as qk
qc = qk.QuantumCircuit(3,3)
for i in range(3):
  qc.h(i)
  qc.measure(i,i)
show_histogram(qc);

from qiskit import QuantumCircuit
qc = QuantumCircuit(4,4)
qc.x([0,1])
qc.cx([0,1],[2,2])
qc.ccx(0,1,3)
qc.measure([0,1,2,3],[0,1,2,3])
show_histogram(qc);

import qiskit as qk
qc = qk.QuantumCircuit(1)
qc.x(0)
qc.measure_all()
show_histogram(qc);

# To avoid modifying the original circuit, let's first copy it
qc2 = qc.copy()
qc2.remove_final_measurements()
qc2.draw('mpl', style='clifford', scale=1)

import qiskit as qk
sv = qk.quantum_info.Statevector(qc2); sv

from qiskit.visualization import plot_bloch_multivector
plot_bloch_multivector(sv)

from qiskit.quantum_info import Operator
Operator(qc2).data