import numpy as np

# Importing standard Qiskit libraries
from qiskit import *
from qiskit.tools.jupyter import *
from qiskit.visualization import *
from ibm_quantum_widgets import *
from qiskit.providers.aer import QasmSimulator

# Loading your IBM Quantum account(s)
provider = IBMQ.load_account()

from qiskit.quantum_info import Statevector

#Defining Quantum circuit
bell0 = QuantumCircuit(2)

#Creating Bell state
bell0.h(0)
bell0.cx(0,1)

#Extracting statevector
sv = Statevector.from_label('00')
sv_ev = sv.evolve(bell0)
sv_ev.draw('latex')

sv_ev.draw('qsphere') 

#Defining Quantum circuit
bell1 = QuantumCircuit(2)

#Creating Bell state
bell1.h(0)
bell1.cx(0,1)

#Extracting statevector
sv1 = Statevector.from_label('01')
sv_ev1 = sv1.evolve(bell1)
sv_ev1.draw('latex')

sv_ev1.draw('qsphere')

#Defining Quantum circuit
bell2 = QuantumCircuit(2)

#Creating Bell state
bell2.x(1)
bell2.h(0)
bell2.cx(0,1)

#Extracting statevector

sv_ev2 = sv.evolve(bell2)
sv_ev2.draw('latex')

sv_ev2.draw('qsphere')

#Defining Quantum circuit
bell3 = QuantumCircuit(2)

#Creating Bell state
bell3.x(0)
bell3.x(1)
bell3.h(0)
bell3.cx(0,1)

#Extracting statevector
sv_ev3 = sv.evolve(bell3)
sv_ev3.draw('latex')

sv_ev3.draw('qsphere')

bell3.draw('text')

qc_ghz = QuantumCircuit(3)
qc_ghz.h(0)
qc_ghz.cx([0,0],[1,2])
qc_ghz.draw('text')

qc_ghz.draw()

sv = Statevector.from_label('000')
sv_ev = sv.evolve(qc_ghz)
sv_ev.draw('latex')

sv = Statevector.from_int(0,2**3)
sv_ev = sv.evolve(qc_ghz)
sv_ev.draw('latex')

BasicAer.backends()

ghz_backend = Aer.get_backend('statevector_simulator')
job = execute(qc_ghz, ghz_backend, shots = 1000)  
result = job.result()
sv_ghz2 = result.get_statevector(qc_ghz) 

plot_state_qsphere(sv_ghz2)


plot_state_city(sv_ghz2, title = 'GHZ')

plot_state_hinton(sv_ghz2, title = 'GHZ')

plot_state_paulivec(sv_ghz2, title = 'GHZ')

plot_bloch_multivector(sv_ghz2, title = 'GHZ')

plot_bloch_vector([0,0.5,1])

q_a = QuantumRegister(1, 'q_a')
q_b = QuantumRegister(1, 'q_b')

qc = QuantumCircuit(q_a,q_b)

qc.h(0)
qc.z(0)

backend = Aer.get_backend('statevector_simulator')
job = execute(qc, backend, shots = 1000)
res = job.result()
sv = res.get_statevector()

#sv.draw('latex')
plot_bloch_multivector(sv)

plot_state_city(sv, color = ['orange', 'black'])

qc.measure_all()

backend = Aer.get_backend('qasm_simulator')
res = execute(qc, backend, shots = 1000).result()
counts = res.get_counts()

plot_histogram(counts)

meas = QuantumCircuit(3,3)
meas.barrier()
meas.measure([0,1,2],[0,1,2])

circ = meas.compose(qc_ghz, range(3), front = True )
circ.draw()

backend = Aer.get_backend('qasm_simulator')
res = backend.run(circ, shots = 1000).result()

counts = res.get_counts()
print(counts)

res2 = execute(circ, backend, shots = 1000).result()
counts2 = res2.get_counts()

print(counts2)

legend = ['Count-1', 'Count-2']
colour = ['teal', 'black']

plot_histogram([counts,counts2], legend=legend, sort='asc', color=colour) #, bar_labels = False)

provider.backends()


















from qiskit.tools import job_monitor

import qiskit.tools.jupyter
#%qiskit_job_watcher

quito = provider.get_backend('ibmq_quito')
job = execute(circ, quito, shots = 1000)
job_monitor(job)

counts3 = job.result().get_counts()
plot_histogram(counts3)

from qiskit.quantum_info import Operator

U = Operator(qc_ghz)
U.data

np.around(U.data, 3)

uni_backend = Aer.get_backend('unitary_simulator')

U = execute(qc_ghz, uni_backend).result().get_unitary(decimals=3)
U

q = QuantumRegister(2, 'q_reg')
c = ClassicalRegister(2, 'c_reg')

qc = QuantumCircuit(q,c)

qc.h([0,1])
qc.cx(q[0],q[1])

qc.draw('mpl')

U_bell = Operator(qc)
np.around(U_bell, 3)

uni_backend = Aer.get_backend('unitary_simulator')
U = execute(qc, uni_backend).result().get_unitary(decimals=3)
U

a = 1/np.sqrt(3)
desired_state = [a, np.sqrt(1-a**2)]

q = QuantumRegister(1, 'q')
qc = QuantumCircuit(q)
qc.initialize(desired_state,0)
qc.draw('mpl')

qc.decompose().draw()

qc.measure_all()
qc.draw()

backend = Aer.get_backend('qasm_simulator')
counts = execute(qc, backend, shots=1000).result().get_counts()
plot_histogram(counts)



back_sv = BasicAer.get_backend('statevector_simulator')
result = execute(qc, back_sv).result()
qc_sv = result.get_statevector()
qc_sv


from qiskit.quantum_info import state_fidelity
state_fidelity(desired_state, qc_sv)

# Gate and prrocess fidility

from qiskit.circuit.library import XGate
from qiskit.quantum_info import Operator, average_gate_fidelity, process_fidelity
op_a = Operator(XGate())
op_b = np.exp(1j / 2) * op_a

#these differ only by a phase so the gate and process
#fidelities are expected to be 1

a = average_gate_fidelity(op_a,op_b)
a

b = process_fidelity(op_a, op_b)
a == b

back = Aer.get_backend('unitary_simulator')
result = execute(qc, back).result()
U_qc = result.get_unitary(decimals = 3)
U_qc

qc = QuantumCircuit(3)
qc.h(0)
qc.cx(0,1)
qc.cx(1,2)
qc.draw()

qc_gate = qc.to_gate()
qc_gate.name = 'Bell State Gate'

circ = QuantumCircuit(3)
circ.append(qc_gate, [0,1,2])
circ.draw()

circ.decompose().draw()

c_qc_gate = qc.to_gate().control(2)
c_qc_gate.name = 'C-Bell State Gate'

circ2 = QuantumCircuit(5)
circ2.append(c_qc_gate, [0,4,1,2,3])
circ2.draw()

circ2.decompose().draw()

#going back to our simpler circuit!
qc = QuantumCircuit(3)
qc.mct([0,1],2)
qc.cx(0,2)
qc.h(1)
qc.z(0)
qc.draw(scale = 1.5, style = {'backgroundcolor': 'lightpink'})

trans = transpile(qc, basis_gates = ['u3','cx','s'])
trans.draw('mpl')

from qiskit.tools.visualization import circuit_drawer
circuit_drawer(qc, output='text')


qc.measure_all()


qasm_str = qc.qasm() #returning a qasm string, THIS SIMPLE
qasm_str

circ = QuantumCircuit.from_qasm_str(qasm_str) #you got to be kidding!
circ.draw()

qc.depth()

qiskit.__version__

#this is your tool to get all the information on all the hardware available, check it for yourself!
import qiskit.tools.jupyter
%qiskit_backend_overview

#that's not a problem!

from qiskit.visualization import plot_gate_map
backend = provider.get_backend('ibmq_manila')
plot_gate_map(backend, plot_directed=True) #check other things you can do in this within docs!

plot_error_map(backend)

qc = QuantumCircuit(3)
qc.h([0,1,2])
qc.measure_all()
sim = BasicAer.get_backend('qasm_simulator')

couple_map = [[0,2],[1,2]] #specify some linear connection
job = execute(qc, sim, shots=1000, coupling_map=couple_map) #here is how you take that in execute also!

result = job.result()
counts = result.get_counts()
print(counts)



