import matplotlib as plt
import numpy
import math
from qiskit import QuantumCircuit, QuantumRegister, assemble, Aer, BasicAer, execute
from qiskit.quantum_info import Statevector, state_fidelity, average_gate_fidelity, process_fidelity, hellinger_fidelity
from qiskit.quantum_info.operators import Operator
from qiskit.visualization import plot_histogram
from qiskit.extensions import XGate

import qiskit.tools.jupyter
%qiskit_version_table

qc = QuantumCircuit(4, 4)
qc.draw('mpl')


qc = QuantumCircuit(4)
qc.draw('mpl')

qc = QuantumCircuit(QuantumRegister(4, 'qr0'), QuantumRegister(4, 'crl'))
qc.draw('mpl')

qc = QuantumCircuit([4, 4])
qc.draw('mpl')

qc = QuantumCircuit(1,1)
qc.ry(3 * math.pi/4, 0)
qc.measure(0,0)
qc.draw('mpl')

qasmsim = Aer.get_backend('qasm_simulator')
qobj = assemble(qc)  # Assemble circuit into a Qobj that can be run
counts = qasmsim.run(qobj).result().get_counts()  # Do the simulation, returning the state vector
plot_histogram(counts)  # Display the output state vector

inp_reg = QuantumRegister(2, name='inp')
ancilla = QuantumRegister(1, name='anc')
qc = QuantumCircuit(inp_reg, ancilla)
qc.h(inp_reg[0:2])
qc.x(ancilla[0])
qc.draw('mpl')

qc = QuantumCircuit(3, 3)
qc.measure([0,1,2], [0,1,2])
qc.draw('mpl')

qc = QuantumCircuit(3, 3)
qc.measure_all()
qc.draw('mpl')

bell = QuantumCircuit(2)
bell.h(0)
bell.x(1)
bell.cx(0, 1)
bell.draw('mpl')


qc = QuantumCircuit(2)
v = [1/math.sqrt(2), 0, 0, 1/math.sqrt(2)]
qc.initialize(v, [0,1])

simulator = Aer.get_backend('statevector_simulator')
result = execute(qc, simulator).result()
statevector = result.get_statevector()
print(statevector)

qc= QuantumCircuit(3, 3)
qc.barrier()
qc.draw('mpl')

qc= QuantumCircuit(3, 3)
qc.barrier(qc)
qc.draw('mpl')

qc= QuantumCircuit(3, 3)
qc.barrier(3)
qc.draw('mpl')

qc = QuantumCircuit(2, 2) 
qc.h(0)
qc.barrier(0)
qc.cx(0,1)
qc.barrier([0,1])
qc.draw('mpl')

print("Circuit depth: ", qc.depth())

qc = QuantumCircuit(3)

# Insert code fragment here
qasm_sim = Aer.get_backend('qasm_simulator')
couple_map = [[0, 1], [1, 2]]
job = execute(qc, backend=qasm_sim, shots=1024, coupling_map=couple_map)

result = job.result()
print(result)

qc = QuantumCircuit(3)

# Insert code fragment here
qasm_sim = Aer.get_backend('ibmq_simulator')
couple_map = [[0, 1], [0, 2]]
job = execute(qc, loop=1024, coupling_map=couple_map)

result = job.result()
print(result)

qc = QuantumCircuit(3)

# Insert code fragment here
qasm_sim = Aer.get_backend('qasm_simulator')
couple_map = [[0, 1], [1, 2]]
job = execute(qc, backend=qasm_sim, repeat=1024, coupling_map=couple_map)

result = job.result()
print(result)

qc = QuantumCircuit(3)

# Insert code fragment here
qasm_sim = Aer.get_backend('qasm_simulator')
couple_map = [[0, 1], [1, 2]]
job = execute(backend=qasm_sim, qc, shot=1024, coupling_map=couple_map)

result = job.result()
print(result)

backend = BasicAer.get_backend('qasm_simulator')
qc = QuantumCircuit(3)
# insert code here
execute(qc, backend, shots=1024, coupling_map=[[0, 1], [1, 2]])

backend = BasicAer.get_backend('qasm_simulator')
qc = QuantumCircuit(3)
# insert code here
execute(qc, backend, shots=1024, custom_topology=[[0, 1], [2, 3]])

backend = BasicAer.get_backend('qasm_simulator')
qc = QuantumCircuit(3)
# insert code here
execute(qc, backend, shots=1024, device="qasm_simulator", mode="custom")

backend = BasicAer.get_backend('qasm_simulator')
qc = QuantumCircuit(3)
# insert code here
execute(qc, backend, mode="custom")

op = Operator.Xop(0)

qc = QuantumCircuit(1)
op = Operator([[0, 1]])
qc.append(op)

qc_a = QuantumCircuit(1)
op_a = Operator(XGate())
qc_a.append(op_a, [0])
state_a = Statevector.from_instruction(qc_a)


qc_b = QuantumCircuit(1)
op_b = numpy.exp(1j * 0.5) * Operator(XGate())
qc_b.append(op_b, [0])
state_b = Statevector.from_instruction(qc_b)

state_fidelity(state_a, state_b)



process_fidelity(op_a, op_b)

average_gate_fidelity(op_a, op_b)

qc = QuantumCircuit(2, 2)
qc.x(0)
qc.measure([0,1], [0,1])
simulator = Aer.get_backend('qasm_simulator')
result = execute(qc, simulator, shots=1000).result()
counts = result.get_counts(qc)
print(counts)

