diff --git a/create_graph_dumb.py b/create_graph_dumb.py new file mode 100644 index 0000000000000000000000000000000000000000..d748761c4f3e2842a299a339473f3476b16e86d8 --- /dev/null +++ b/create_graph_dumb.py @@ -0,0 +1,73 @@ +import random +import argparse + + +def create_random_graph(args): + nb_nodes = args.nodes + nb_links = args.links + + nodes = dict() + for _ in range(nb_links): + node_a = random.randint(0, nb_nodes - 1) + node_b = random.randint(0, nb_nodes - 1) + while node_b == node_a: + node_b = random.randint(0, nb_nodes - 1) + c_ab = random.randint(1, 10) + + nodes.setdefault(node_a, list()).append((node_b, c_ab)) + + return nodes, nb_links + + +def ntf_parse(args): + with open(args.ntf) as fd: + data = fd.read().split("\n") + + mapping = dict() + nodes = dict() + for line in data: + tab = line.split(" ") + node_a = tab[0] + node_b = tab[1] + + node_a = mapping.setdefault(node_a, len(mapping)) + node_b = mapping.setdefault(node_b, len(mapping)) + + c_ab = int(tab[2]) + nodes.setdefault(node_a, list()).append((node_b, c_ab)) + + return nodes, len(data) + + +def to_binary_file(nodes, nb_links, output): + nb_nodes = len(nodes) + with open(output, "wb+") as fd: + fd.write(nb_nodes.to_bytes(4, "big")) + fd.write(nb_links.to_bytes(4, "big")) + + print(nodes) + for node in nodes: + for j, cost in nodes[node]: + fd.write(node.to_bytes(4, "big")) + fd.write(j.to_bytes(4, "big")) + fd.write(cost.to_bytes(4, "big")) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--ntf", type=str, default=None, + help="Parse an NTF file instead of creating a graph") + parser.add_argument("--output", type=str, + help="Output file", default="graph.bin") + parser.add_argument("-n", "--nodes", type=int, + help="Number of nodes. Unused if '--ntf'", default=5) + parser.add_argument("-l", "--links", type=int, + help="Number of links. Unused if '--ntf'", default=10) + args = parser.parse_args() + + if args.ntf: + graph, nb_links = ntf_parse(args) + else: + graph, nb_links = create_random_graph(args) + + to_binary_file(graph, nb_links, args.output) diff --git a/shortest_path.py b/shortest_path.py new file mode 100644 index 0000000000000000000000000000000000000000..bd1ba825c682c6d716ceffa8819a1ef890dc31a7 --- /dev/null +++ b/shortest_path.py @@ -0,0 +1,122 @@ +import numpy as np +import math + +import argparse +import sys +import os + + + +parser = argparse.ArgumentParser(description="LEPL1503 - Détection et correction d'erreurs") +parser.add_argument("input_file", help="Nom du fichier contenant les données nécessaires") +parser.add_argument("-f", help="Chemin vers le fichier de sortie", type=argparse.FileType("wb"), default=sys.stdout) +parser.add_argument("-v", help="\"verbose\" mode: si ajouté, affiche des informations sur l'exécution du programme", action="store_true") +args = parser.parse_args() + +verbose = args.v +output_fd = args.f +nb_nodes = None +nb_edges = None + +if verbose: + print(args, file=sys.stderr) + +def get_file_infos(data): + nb_nodes = int.from_bytes(data[0:4], "big",signed=True) + nb_edges = int.from_bytes(data[4:], "big",signed=True) + return nb_nodes,nb_edges + +def bellman_ford(table,s): + dist = [math.inf]*nb_nodes + dist[s]=0 + path = [-1]*nb_nodes + for _ in range(nb_nodes-1): + for j in range(len(table)): + a,b,cab = table[j][0],table[j][1],table[j][2] + if(dist[a]!=math.inf and dist[b]>dist[a]+cab): + dist[b]=dist[a]+cab + path[b]=a + for j in range(len(table)): + a,b,cab = table[j][0],table[j][1],table[j][2] + if(dist[a]!=math.inf and dist[b]>dist[a]+cab): + print("Cycle négatif détecté") + return -1, -1 + return dist, path + +def get_path(dest, path,source): + r = [dest] + i = dest + while(True): + if (i ==source): + break + r.insert(0,path[i]) + i = path[i] + return r + +def get_max(dist,s): + max = -math.inf + n = s + for i in range(len(dist)): + if(i!=s and dist[i]!=math.inf and dist[i]>=max): + max = dist[i] + n=i + if(max==-math.inf): + if(dist[s]!=math.inf and dist[s]>=max): + max = dist[s] + + return max,n +if __name__ == "__main__": + + with open(args.input_file, "rb") as input_file: + binary_data = input_file.read() + nb_nodes, nb_edges = get_file_infos(binary_data[:8]) + if verbose: + print("Number of nodes :", nb_nodes, ", number of links :", nb_edges) + + binary_data = binary_data[8:] + table = [] + if output_fd == sys.stdout or output_fd == sys.stderr: + print(nb_nodes) + + else : + output_fd.write(nb_nodes.to_bytes(4, "big")) + + + for i in range(nb_edges): + a = int.from_bytes(binary_data[i*16:i*16+4],"big",signed=True) + b = int.from_bytes(binary_data[i*16+4:i*16+8],"big",signed=True) + cab = int.from_bytes(binary_data[i*16+8:i*16+12],"big",signed=True) + cba = int.from_bytes(binary_data[i*16+12:i*16+16],"big",signed=True) + if (cab !=0): + l1 = [a,b,cab] + table.append(l1) + if(cba!=0): + l2 = [b,a,cba] + table.append(l2) + for i in range(nb_nodes): + dist, path = bellman_ford(table,i) + if dist ==-1 : break + + if output_fd == sys.stdout or output_fd == sys.stderr: + print("source : "+str(i)) + d,n = get_max(dist,i) + print("destination : "+ str(n)) + print("cout : "+ str(d)) + p = get_path(n,path,i) + print("nombre de noeuds : "+str(len(p))) + print("chemin : "+" ".join(str(x) for x in p)) + print("-----------------------") + + else : + output_fd.write(i.to_bytes(4, "big")) + d,n = get_max(dist,i) + output_fd.write(n.to_bytes(4, "big",signed=True)) + output_fd.write(d.to_bytes(4, "big",signed=True)) + r = get_path(n,path,i) + output_fd.write(len(r).to_bytes(4, "big",signed=True)) + for j in range(len(r)) : + output_fd.write(r[j].to_bytes(4, "big")) + + + + \ No newline at end of file diff --git a/verify_output.py b/verify_output.py new file mode 100644 index 0000000000000000000000000000000000000000..f8a7e655ef9a03b808b6df76cbb3f73f1bc7e549 --- /dev/null +++ b/verify_output.py @@ -0,0 +1,47 @@ +import argparse +import struct + + +def verify_output(file): + with open(file, "rb") as fd: + # First 4 bytes should be the number of nodes + data = fd.read(4) + nb_nodes, = struct.unpack(">l", data) + + # The file should contain exactly nb_nodes entries + for _ in range(nb_nodes): + # An entry is 4 + 4 + 4 + 4 + len(path) * 4 bytes + data = fd.read(16) + + # Index of the node, distance value, path length (number of hops) + source_idx, node_idx, a, path_len = struct.unpack(">llll", data) + + # The node index lies within the limits. + assert source_idx >= 0 and source_idx < nb_nodes + assert node_idx >= 0 and node_idx < nb_nodes + + # The path len can be nul if there is no path. + # The shortest path cannot contain loops. + assert path_len >= -1 and path_len < nb_nodes + + if path_len > 0: + for _ in range(path_len): + data = fd.read(4) + hop_idx, = struct.unpack(">l", data) + + # Same... the node index lies within the limits. + assert hop_idx >= 0 and hop_idx < nb_nodes + + # The file does not contain anymore bytes + assert fd.read() == b"" + + print("The file has the correct format!\nThis does not mean that it solves the shortest path problem, but at least it contains readable information...") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "file", type=str, help="Binary file generated by the project to inspect") + args = parser.parse_args() + + verify_output(args.file) diff --git a/visualize_graph.py b/visualize_graph.py new file mode 100644 index 0000000000000000000000000000000000000000..40558600d951aafff49da826fa415b56f048c69f --- /dev/null +++ b/visualize_graph.py @@ -0,0 +1,44 @@ +import graphviz +import struct +import argparse +import os + + +def read_graph(filename): + with open(filename, "rb") as fd: + data = fd.read(8) + _, nb_links = struct.unpack(">ll", data) + + graph = dict() + + for _ in range(nb_links): + data = fd.read(12) + node_1, node_2, cost_12 = struct.unpack(">lll", data) + graph.setdefault(node_1, list()).append((node_2, cost_12)) + + return graph + + +def plot_graph(graph, output_filepath): + file, file_extension = os.path.splitext(output_filepath) + g = graphviz.Graph(format=file_extension[1:]) + + for node in graph: + for nei_id, cost in graph[node]: + g.node(f"{node}", label=f"{node}") + g.node(f"{nei_id}", label=f"{nei_id}") + g.edge(f"{node}", f"{nei_id}", label=f"{cost}", dir="forward") + + g.render(file) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "file", type=str, help="Input file (binary) representing the graph") + parser.add_argument("save", type=str, + help="Save the graph visualization in the indicated path") + args = parser.parse_args() + + graph = read_graph(args.file) + plot_graph(graph, args.save)