from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
import numpy as np
from qiskit.circuit.random import random_circuit
from qiskit.circuit import Parameter
from qiskit import IBMQ
from qiskit.compiler import transpile
from qiskit.converters import circuit_to_dag
from qiskit.tools.visualization import dag_drawer

IBMQ.load_account() # Load account from disk
provider = IBMQ.get_provider(hub='ibm-q')


n = 3 #number of qubits

def superpos(qc,qr):
    for q in qr:
        qc.h(q)

qreg_q = QuantumRegister(n, 'q')
creg_c = ClassicalRegister(n, 'c')
circuit = QuantumCircuit(qreg_q, creg_c)
paras = [Parameter('p'+str(i)) for i in range(n)]

superpos(circuit,qreg_q)
for p in paras:
    circuit.rz(p,int(p.name[1:]))
algorithm = random_circuit(n,n)
circuit += algorithm
superpos(circuit,qreg_q)
circuit.measure(qreg_q,creg_c)
circuit.draw(output='mpl')

circuit_fixed = circuit.bind_parameters({p : np.pi * np.random.uniform(0, 2) for p in paras})
# note : compilation before binding has a better runtime

# transpile the circuit for backend chip
circuit_transpiled = transpile(circuit, backend=provider.get_backend('ibmq_16_melbourne'), optimization_level=3)

print('gates = ', circuit_transpiled.count_ops())
print('depth = ', circuit_transpiled.depth())

# use appropriate optimization level

for opt_lvl in range(4):
    circuit_transpiled = transpile(circuit, backend=provider.get_backend('ibmq_santiago'), optimization_level=opt_lvl)
    print("optimization level ",opt_lvl)
    print('gates = ', circuit_transpiled.count_ops())
    print('depth = ', circuit_transpiled.depth())


circuit_transpiled.draw(output = 'mpl')

dag = circuit_to_dag(circuit)
dag_drawer(dag)

