from collections import Counter
import io, pydot
from PIL import Image
import networkx as nx
import retworkx as rx

from qiskit.test.mock import FakeMumbai, FakeManhattan
from qiskit.providers.aer.noise import NoiseModel

mumbai = FakeMumbai() 
conf = mumbai.configuration()
props = mumbai.properties()
noise_model = NoiseModel.from_backend(mumbai)
graph = rx.PyGraph(multigraph=False)
coupling_map = [tuple(edge) for edge in conf.coupling_map]
graph.add_nodes_from([i for i in range(27)])
graph.add_edges_from_no_data(coupling_map)
circle_nodes = [12, 13, 14, 16, 19, 22, 25, 24, 23, 21, 18, 15]
neighbor_nodes = [10, 11, 20, 26, 17]

def node_attr(node):
    kwargs = dict(style='filled', shape="circle", label="", fixedsize="true", width="0.3", height="0.3")
    if node in circle_nodes:
        kwargs.update(fillcolor='green')
    elif node in neighbor_nodes:
        kwargs.update(fillcolor='lightblue')
    else:
        kwargs.update(fillcolor='darkblue')
    return kwargs
graph_attr = {"center": "true", "mode": "KK", "ratio": "0.5"}

dot_str = graph.to_dot(node_attr=node_attr, graph_attr=graph_attr)
dot = pydot.graph_from_dot_data(dot_str)[0]
png = dot.create_png(prog='neato')
Image.open(io.BytesIO(png))

print(f"Mumbai has the following gate set: {set(noise_model.basis_gates) - set(['reset'])}") # reset not physically implemented yet
num_qubits = conf.n_qubits
def calc_mean(T: callable) -> float:
    """ return mean of T1 or T2 time in microseconds"""
    s = sum(T(qubit) for qubit in range(num_qubits))
    return s / num_qubits * 10**6
T1, T2 = map(calc_mean, (props.t1, props.t2))
print(f"Averages over all {num_qubits} qubits in µs: {T1=:.2f} {T2=:.2f}")
mean_readout = sum(props.readout_error(q) for q in range(num_qubits)) / num_qubits * 100
print(f"Average readout error is {mean_readout:.2f} %")

manhattan = FakeManhattan()
conf = manhattan.configuration()
G = nx.Graph()
G.add_nodes_from(range(0,conf.n_qubits))
G.add_edges_from(conf.coupling_map)
print(f"Manhattan Graph: Nodes {G.number_of_nodes()}   Connections {G.number_of_edges()}")
degree_ct = Counter()
for node, degree in dict(G.degree()).items():
    degree_ct[degree] += 1
print("Degree to number of nodes: ", degree_ct)

