#!/usr/bin/python3 from random import randint, random import networkx as nx import sys class Node(object): def __init__(self, id, g): self.graph = g self.id = id self.color = None self.candidate_list = None self.queue = "start" self.next_tick = None self.edges = [] self.done = False def add_edge(self, node): self.edges.append(node) def print(self): print(f"Node {self.id} = (c:{self.color}, #e:{len(self.edges)})") def run_tick(self): if not self.done: # candidate list of colors self.candidate_list = self.graph.possible_colors.copy() if not self.next_tick == "start": # if its not the first iteration w/o data, filter out all neighbor colors from candidate list neighbor_candidates = self.next_tick.copy() for colore in neighbor_candidates: if colore in self.candidate_list: self.candidate_list.remove(colore) # if color is in candidate list (no neighbors has this color), finish. # do not run this at start if self.color in self.candidate_list and self.color is not None: self.done = True self.queue = "stop" #print(f"{self.id} | is done") return # pick random color from candidate list self.color = self.candidate_list[randint(0, len(self.candidate_list) - 1)] #print(f"{self.id} | possible colors: {self.candidate_list}, current color: {self.color}") # send data to the other neighbors for n in self.edges: if n.queue != "stop": n.queue.append(self.color) #print(f"{self.id} => sending {self.color} to N[{n.id}]") class Graph(object): def __init__(self): self.V = None self.max_degree = None def load_graph_file(self, graph_file): with open(graph_file, "r") as f: V_amt, E_amt = map(int, f.readline().split(" ")) self.V = [Node(x, self) for x in range(1, V_amt+1)] self.E = {} self.max_degree = 0 for i in range(0, E_amt): a, b = map(int, f.readline().split(" ")) a -= 1 b -= 1 self.V[a].add_edge(self.V[b]) self.V[b].add_edge(self.V[a]) self.max_degree = max(self.max_degree, len(self.V[a].edges), len(self.V[b].edges)) self.possible_colors = [x for x in range(1, self.max_degree + 2)] def load_graph_networkx(self, graph): self.V = [Node(x, self) for x in range(1, graph.number_of_nodes() + 1)] self.E = {} self.max_degree = 0 for a, b in graph.edges: self.V[a].add_edge(self.V[b]) self.V[b].add_edge(self.V[a]) self.max_degree = max(self.max_degree, len(self.V[a].edges), len(self.V[b].edges)) self.possible_colors = [x for x in range(1, self.max_degree + 2)] def run_tick(self): print("Next Tick") # send data for n in self.V: n.next_tick = n.queue n.queue = [] # run tick for each for n in self.V: n.run_tick() done = True for n in self.V: done &= n.done if done: print("Stop") return -1 pass def print_graph(self): print(f"Max Degree: {self.max_degree}") for n in self.V: n.print() def check_correctness(self): for n in self.V: color = n.color for neighbor in n.edges: if neighbor.color == color: print("=====================") n.print() neighbor.print() print(f"Neighbor {neighbor.id} has same color as {n.id}") print("=====================") return False return True def main(): g = Graph() if len(sys.argv) == 2: filename = sys.argv[1] g.load_graph(filename) else: G = nx.erdos_renyi_graph(randint(100, 300), random()) g.load_graph_networkx(G) g.print_graph() for i in range(0, 10): ret = g.run_tick() if ret == -1: # print info print("Final colors:") for n in g.V: n.print() # assert if correct assert g.check_correctness() exit(0) pass if __name__ == '__main__': main()