import numpy as np
from qiskit import *
%matplotlib inline

# Create a Quantum Circuit acting on a quantum register of three qubits
circ = QuantumCircuit(3)

# Add a H gate on qubit 0, putting this qubit in superposition.
circ.h(0)
# Add a CX (CNOT) gate on control qubit 0 and target qubit 1, putting
# the qubits in a Bell state.
circ.cx(0, 1)
# Add a CX (CNOT) gate on control qubit 0 and target qubit 2, putting
# the qubits in a GHZ state.
circ.cx(0, 2)

#circ.draw()
circ.draw(output='mpl')

# Import Aer
from qiskit import Aer

# Run the quantum circuit on a statevector simulator backend
backend = Aer.get_backend('statevector_simulator')

# Create a Quantum Program for execution 
job = execute(circ, backend)

result = job.result()

outputstate = result.get_statevector(circ, decimals=3)
print(outputstate)

from qiskit.visualization import plot_state_city
plot_state_city(outputstate)

# Run the quantum circuit on a unitary simulator backend
backend = Aer.get_backend('unitary_simulator')
job = execute(circ, backend)
result = job.result()

# Show the results
print(result.get_unitary(circ, decimals=3))

# Create a Quantum Circuit
meas = QuantumCircuit(3, 3)
meas.barrier(range(3))
# map the quantum measurement to the classical bits
meas.measure(range(3),range(3))

# The Qiskit circuit object supports composition using
# the addition operator.
qc = circ+meas

#drawing the circuit
qc.draw()

# Use Aer's qasm_simulator
backend_sim = Aer.get_backend('qasm_simulator')

# Execute the circuit on the qasm simulator.
# We've set the number of repeats of the circuit
# to be 1024, which is the default.
job_sim = execute(qc, backend_sim, shots=1024)

# Grab the results from the job.
result_sim = job_sim.result()

counts = result_sim.get_counts(qc)
print(counts)

from qiskit.visualization import plot_histogram
plot_histogram(counts)

from qiskit import IBMQ

#IBMQ.save_account('APIKEY')

IBMQ.load_account()

IBMQ.providers()

provider = IBMQ.get_provider(group='open')

provider.backends()

simulator_backend = provider.get_backend('ibmq_qasm_simulator')

job_cloud = execute(qc, backend=simulator_backend)

result_cloud = job_cloud.result()

counts_cloud = result_cloud.get_counts(qc)
plot_histogram(counts_cloud)

#example of backend filtering, you can modify the filters or ask just for the least_busy device
from qiskit.providers.ibmq import least_busy
backend = least_busy(provider.backends(filters=lambda x: x.configuration().n_qubits <= 5 and 
                                   not x.configuration().simulator and x.status().operational==True))
print("least busy backend: ", backend)

#here we ask for the backend properties
backend.configuration()

from qiskit.tools.monitor import job_monitor

job_exp = execute(qc, backend=backend)
job_monitor(job_exp)

result_exp = job_exp.result()

counts_exp = result_exp.get_counts(qc)
plot_histogram([counts_exp,counts], legend=['Device', 'Simulator'])

#uncomment the line below in case you are not able to specify 'latex' in the draw method
#!pip install pylatexenc
#!pip install pillow

from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit import BasicAer

qr0 = QuantumRegister(1,name='qb')  # qb for "qubit", but the name is optional
cr0 = ClassicalRegister(1,name='b') # b for "bit", but the name is optional
qc0 = QuantumCircuit(qr0,cr0)


qc0.draw(output='latex')


from qiskit.tools.visualization import plot_bloch_vector
import math

theta = math.pi/2    # You can try to change the parameters theta and phi
phi = math.pi/4      # to see how the vector moves on the sphere
plot_bloch_vector([math.cos(phi)*math.sin(theta),math.sin(phi)*math.sin(theta),math.cos(theta)])  # Entries of the 
                                                                                                  # input vector are 
                                                                                                  # coordinates [x,y,z]

qc0.measure(qr0,cr0)
#Select the output method to use for drawing the circuit. Valid choices are text, latex, latex_source, or mpl. By default the ‘text’ drawer is used unless a user config file has an alternative backend set as the default. 
#If the output kwarg is set, that backend will always be used over the default in a user config file.
qc0.draw('latex')

#backend_sim = Aer.get_backend('qasm_simulator') already defined above
job = execute(qc0, backend_sim, shots=1024)
result = job.result()
counts = result.get_counts()
print(counts)

qc1 = QuantumCircuit(qr0,cr0)
qc1.x(0) # A X gate targeting the first (and only) qubit in register qr0
qc1.measure(qr0,cr0)

qc1.draw(output='mpl')

job1 = execute(qc1, backend_sim, shots=1024)
result1 = job1.result()
counts1 = result1.get_counts()
print(counts1)

from qiskit.tools.visualization import plot_bloch_multivector

vec_backend = Aer.get_backend('statevector_simulator')
result_vec0 = execute(qc0, vec_backend).result()
result_vec1 = execute(qc1, vec_backend).result()
psi0 = result_vec0.get_statevector()
psi1 = result_vec1.get_statevector()

plot_bloch_multivector(psi0)

plot_bloch_multivector(psi1)

qc2 = QuantumCircuit(qr0,cr0)
qc2.h(0)
qc2.measure(qr0,cr0)

qc2.draw(output='mpl')

job2 = execute(qc2, backend_sim, shots=1024)
result2 = job2.result()
counts2 = result2.get_counts()
print(counts2)

result_vec2 = execute(qc2, vec_backend).result()
psi2 = result_vec2.get_statevector()
plot_bloch_multivector(psi2)

qc3 = QuantumCircuit(qr0,cr0)
qc3.h(0)
qc3.s(0) # Use sdg instead of s for the adjoint
qc3.measure(qr0,cr0)

qc3.draw(output='mpl')

job3 = execute(qc3, backend_sim, shots=1024)
result3 = job3.result()
counts3 = result3.get_counts()
print(counts3)

result_vec3 = execute(qc3, vec_backend).result()
psi3 = result_vec3.get_statevector()
plot_bloch_multivector(psi3)

qc4 = QuantumCircuit(qr0,cr0)
qc4.h(0)
qc4.t(0) # Use tdg instead of t for the adjoint
qc4.measure(qr0,cr0)

qc4.draw(output='mpl')

job4 = execute(qc4, backend_sim, shots=1024)
result4 = job4.result()
counts4 = result4.get_counts()
print(counts4)

result_vec4 = execute(qc4, vec_backend).result()
psi4 = result_vec4.get_statevector()
plot_bloch_multivector(psi4)

qc4b = QuantumCircuit(qr0)
qc4b.tdg(0)
qc4b.draw('mpl')

backend_unit = BasicAer.get_backend('unitary_simulator')
job = execute(qc4b, backend_unit)
job.result().get_unitary(qc4b, decimals=3)

lmbd = 1.2  # Change this value to rotate the output vector on the equator of the Bloch sphere
qc5 = QuantumCircuit(qr0,cr0)
qc5.h(0)
qc5.u1(lmbd,0)
qc5.measure(qr0,cr0)

qc5.draw(output='mpl')

result_vec5 = execute(qc5, vec_backend).result()
psi5 = result_vec5.get_statevector()
plot_bloch_multivector(psi5)

qc6 = QuantumCircuit(qr0,cr0)
qc6.u2(0,math.pi, 0)
qc6.measure(qr0,cr0)

result_vec6 = execute(qc6, vec_backend).result()
psi6 = result_vec6.get_statevector()
plot_bloch_multivector(psi6)

theta = math.pi
phi = math.pi
lmb = math.pi

qc7 = QuantumCircuit(qr0,cr0)
qc7.u3(theta,phi, lmb, 0)
qc7.measure(qr0,cr0)

result_vec7 = execute(qc7, vec_backend).result()
psi7 = result_vec7.get_statevector()
plot_bloch_multivector(psi7)

qr = QuantumRegister(2,name='qr')  # we need a 2-qubit register now
cr = ClassicalRegister(2,name='cr')
cnot_example = QuantumCircuit(qr,cr)

cnot_example.x(0)
cnot_example.cx(0,1) # First entry is control, the second is the target
cnot_example.measure(qr,cr)

cnot_example.draw(output='mpl')

job_cnot = execute(cnot_example, backend_sim, shots=1024)
result_cnot = job_cnot.result()
counts_cnot = result_cnot.get_counts()
print(counts_cnot)

cnot_reversed = QuantumCircuit(qr,cr)

cnot_reversed.x(0)
# This part uses cx(qr[1],qr[0]) but is equivalent to cx(qr[0],qr[1])
cnot_reversed.h(0)
cnot_reversed.h(1)
cnot_reversed.cx(1,0)
cnot_reversed.h(0)
cnot_reversed.h(1)
#
cnot_reversed.measure(qr,cr)

cnot_reversed.draw(output='mpl')

job_cnot_rev = execute(cnot_reversed, backend_sim, shots=1024)
result_cnot_rev = job_cnot_rev.result()
counts_cnot_rev = result_cnot_rev.get_counts()
print(counts_cnot_rev)

bell_phi_p = QuantumCircuit(qr,cr)

bell_phi_p.h(0)
bell_phi_p.cx(0,1)
bell_phi_p.measure(qr,cr)

bell_phi_p.draw(output='mpl')

job_bell_phi_p = execute(bell_phi_p, backend_sim, shots=1024)
result_bell_phi_p = job_bell_phi_p.result()
counts_bell_phi_p = result_bell_phi_p.get_counts()
print(counts_bell_phi_p)

cz_example = QuantumCircuit(qr)

cz_example.cz(0,1)

cz_example.draw(output='mpl')

job_cz = execute(cz_example, backend_unit)
job_cz.result().get_unitary(cz_example, decimals=3)

cz_from_cnot = QuantumCircuit(qr)
#notice here a more verbose way of writing gates and assign them to qubits
cz_from_cnot.h(qr[1])
cz_from_cnot.cx(qr[0],qr[1])
cz_from_cnot.h(qr[1])

cz_from_cnot.draw(output='mpl')

swap_test = QuantumCircuit(qr)

swap_test.swap(qr[0],qr[1])

swap_test.draw(output='mpl')

swap_from_cnot = QuantumCircuit(qr)

swap_from_cnot.cx(qr[0],qr[1])
swap_from_cnot.cx(qr[1],qr[0])
swap_from_cnot.cx(qr[0],qr[1])

swap_from_cnot.draw(output='mpl')

qr_many = QuantumRegister(5)
control_example = QuantumCircuit(qr_many)

control_example.cy(qr_many[0],qr_many[1])
control_example.ch(qr_many[1],qr_many[2])
control_example.crz(0.2,qr_many[2],qr_many[3])
control_example.cu3(0.5, 0, 0, qr_many[3],qr_many[4])

control_example.draw(output='mpl')

qrthree = QuantumRegister(3)
toffoli_example = QuantumCircuit(qrthree)

toffoli_example.ccx(0,1,2)

toffoli_example.draw(output='mpl')

toffoli_from_cnot = QuantumCircuit(qrthree)

toffoli_from_cnot.h(qrthree[2])
toffoli_from_cnot.cx(qrthree[1],qrthree[2])
toffoli_from_cnot.tdg(qrthree[2])
toffoli_from_cnot.cx(qrthree[0],qrthree[2])
toffoli_from_cnot.t(qrthree[2])
toffoli_from_cnot.cx(qrthree[1],qrthree[2])
toffoli_from_cnot.tdg(qrthree[2])
toffoli_from_cnot.cx(qrthree[0],qrthree[2])
toffoli_from_cnot.tdg(qrthree[1])
toffoli_from_cnot.t(qrthree[2])
toffoli_from_cnot.h(qrthree[2])
toffoli_from_cnot.cx(qrthree[0],qrthree[1])
toffoli_from_cnot.tdg(qrthree[1])
toffoli_from_cnot.cx(qrthree[0],qrthree[1])
toffoli_from_cnot.s(qrthree[1])
toffoli_from_cnot.t(qrthree[0])

toffoli_from_cnot.draw(output='mpl')

# Initializing a three-qubit quantum state
import math
desired_vector = [
    1 / math.sqrt(16) * complex(0, 1),
    1 / math.sqrt(8) * complex(1, 0),
    1 / math.sqrt(16) * complex(1, 1),
    0,
    0,
    1 / math.sqrt(8) * complex(1, 2),
    1 / math.sqrt(16) * complex(1, 0),
    0]


q = QuantumRegister(3)

qc = QuantumCircuit(q)

qc.initialize(desired_vector, [q[0],q[1],q[2]])
qc.draw(output='latex')

backend = BasicAer.get_backend('statevector_simulator')
job = execute(qc, backend)
qc_state = job.result().get_statevector(qc)
qc_state 

state_fidelity(desired_vector,qc_state)