from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.tools.qi.qi import qft
import numpy as np
import copy

# Setting up a Hadamard QPE circuit for demonstrations below.
def h_qpe(circ, q, n):
    for i in range(n-1):
        circ.h(q[i])
    for j in range(0, n-1, 2): # Only place a CH^n on every other qubit, because CX^n = I for n even
        circ.ch(q[j], q[n-1])
    circ.barrier()
    qft(circ, q, n-1)
    
n = 5
qr = QuantumRegister(n)
cr = ClassicalRegister(n)
circuit = QuantumCircuit(qr, cr)
circuit.rx(np.pi/4, qr[n-1])
circuit.barrier()
h_qpe(circuit, qr, n)
unt_circ = copy.deepcopy(circuit)
circuit.barrier()
circuit.measure(qr, cr)

%%capture --no-display
from qiskit import BasicAer, LegacySimulators, Aer

qasm_backend = BasicAer.get_backend('qasm_simulator')
sv_backend = BasicAer.get_backend('statevector_simulator')
unt_backend = BasicAer.get_backend('unitary_simulator')

# Setup
from qiskit import execute

qasm_job = execute(circuit, qasm_backend)
sv_job = execute(unt_circ, sv_backend)
unt_job = execute(unt_circ, unt_backend)
qasm_result = qasm_job.result()
sv_result = sv_job.result()
unt_result = unt_job.result()

qasm_result.data()

sv_result.data()

unt_result.data()
# Not displayed because results are too large for github.

qasm_result.get_counts()

sv_result.get_statevector()

unt_result.get_unitary()

len(sv_result.get_statevector())

from qiskit import LegacySimulators
from qiskit.extensions.simulator.snapshot import snapshot
snap_backend = LegacySimulators.get_backend('statevector_simulator')

snap_unt_circ = copy.deepcopy(unt_circ)
snap_unt_circ.snapshot(10)
sv_job = execute(snap_unt_circ, snap_backend)
sv_result = sv_job.result()
sv_result.data()['snapshots']

from qiskit.quantum_info.analyzation.average import average_data

counts = qasm_result.get_counts()
iden = np.eye(len(counts))
oper = {}
for i, key in enumerate(counts.keys()):
    oper[key] = iden[i]
average_data(counts, oper)

#!pip install qiskit-terra[visualization]

from qiskit.tools.visualization import circuit_drawer
from qiskit.tools.visualization.dag_visualization import dag_drawer

lin_len = 98
# Change to wider in Jupyter, this is only to render nicely in github
circuit.draw(line_length=lin_len)

style = {'cregbundle': True, 'usepiformat': True, 'subfontsize': 12, 'fold': 100, 'showindex': True,
         'backgroundcolor': '#fffaff',
         "displaycolor": { # Taken from qx_color_scheme() in _circuit_visualization.py
            "id": "#ffca64",
            "u0": "#f69458",
            "u1": "#f69458",
            "u2": "#f69458",
            "u3": "#f69458",
            "x": "#a6ce38",
            "y": "#a6ce38",
            "z": "#a6ce38",
            "h": "#00bff2",
            "s": "#00bff2",
            "sdg": "#00bff2",
            "t": "#ff6666",
            "tdg": "#ff6666",
            "rx": "#ffca64",
            "ry": "#ffca64",
            "rz": "#ffca64",
            "reset": "#d7ddda",
            "target": "#00bff2",
            "meas": "#f070aa"}}
circuit.draw(output='mpl', style=style)

circuit.draw(output='latex_source', plot_barriers=False)

circuit.draw(output='latex', plot_barriers=False)

from qiskit import transpiler

draw_circ = transpiler.transpile(circuit, qasm_backend, basis_gates='U,CX')
draw_circ.draw(line_length=2000)
# Not shown in github for rendering reasons, load in Jupyter

from qiskit.tools.qi.qi import outer
from qiskit.tools.visualization import plot_histogram, plot_state_city, plot_bloch_multivector, plot_state_paulivec, plot_state_hinton, plot_state_qsphere
from qiskit.tools.visualization import iplot_histogram, iplot_state_city, iplot_bloch_multivector, iplot_state_paulivec, iplot_state_hinton, iplot_state_qsphere

counts = qasm_result.get_counts()
phi = sv_result.get_statevector()
rho = outer(phi)

plot_histogram(counts)

plot_state_city(rho)

plot_state_hinton(rho)

hist_fig = plot_histogram(counts)
state_fig = plot_state_city(rho)
hist_fig.savefig('histogram.png')
state_fig.savefig('state_plot.png')

iplot_bloch_multivector(rho)
# Not displayed in github, download and run loacally.

from qiskit.converters.circuit_to_dag import circuit_to_dag

my_dag = circuit_to_dag(circuit)
dag_drawer(my_dag)

job = execute(circuit, qasm_backend, shots=50, memory=True)

result = job.result()
result.get_memory(circuit)

from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import BasicSwap, CXCancellation, LookaheadSwap, StochasticSwap
from qiskit.transpiler import transpile
from qiskit.mapper import CouplingMap

qr = QuantumRegister(7, 'q')
qr = QuantumRegister(7, 'q')
tpl_circuit = QuantumCircuit(qr)
tpl_circuit.h(qr[3])
tpl_circuit.cx(qr[0], qr[6])
tpl_circuit.cx(qr[6], qr[0])
tpl_circuit.cx(qr[0], qr[1])
tpl_circuit.cx(qr[3], qr[1])
tpl_circuit.cx(qr[3], qr[0])
tpl_circuit.draw()

coupling = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6]]

simulator = BasicAer.get_backend('qasm_simulator')
coupling_map = CouplingMap(couplinglist=coupling)
pass_manager = PassManager()
pass_manager.append([BasicSwap(coupling_map=coupling_map)])
basic_circ = transpile(tpl_circuit, simulator, pass_manager=pass_manager)
basic_circ.draw()

pass_manager = PassManager()
pass_manager.append([LookaheadSwap(coupling_map=coupling_map)])
lookahead_circ = transpile(tpl_circuit, simulator, pass_manager=pass_manager)
lookahead_circ.draw()

pass_manager = PassManager()
pass_manager.append([StochasticSwap(coupling_map=coupling_map)])
stoch_circ = transpile(tpl_circuit, simulator, pass_manager=pass_manager)
stoch_circ.draw()

qasm_job = execute(circuit, qasm_backend, pass_manager=PassManager())

from qiskit.converters.dag_to_circuit import dag_to_circuit

new_circ_from_dag = dag_to_circuit(my_dag)
new_circ_from_dag.draw(line_length = lin_len)

from qiskit import QuantumCircuit

qasm_str = circuit.qasm()
new_circ = QuantumCircuit.from_qasm_str(qasm_str)
new_circ.draw(line_length = lin_len)

filepath = 'my_qasm_file.txt'
qasm_file = open(filepath, "w")
qasm_file.write(unt_circ.qasm())
qasm_file.close()

new_unt_circ = QuantumCircuit.from_qasm_file(filepath)
new_unt_circ.draw(line_length = lin_len)