def generateTree(G): edges = [] for line in nx.generate_edgelist(G, data=['weight']): vals = line.split(" ") if len(vals) == 3: edges.append((int(vals[0]), int(vals[1]), float(vals[2]))) new_Graph = nx.Graph() for i in G.nodes(): new_Graph.add_node(i) G_edges = edges while not nx.is_tree(new_Graph) and not is_valid_network( G, new_Graph): #nx.number_connected_components(new_Graph) != 1: n = np.random.choice(range(len(G_edges))) edgeVal = G_edges[n] new_Graph.add_edge(edgeVal[0], edgeVal[1], weight=edgeVal[2]) has_cycle = True try: nx.find_cycle(new_Graph) except: has_cycle = False if has_cycle == True: new_Graph.remove_edge(edgeVal[0], edgeVal[1]) else: G_edges.remove(edgeVal) assert nx.is_tree(new_Graph) assert is_valid_network(G, new_Graph) return new_Graph
def find_tree(n, T_copy, G): if n == 0: return T.copy() iters = 0 # print(n, len(T_copy.nodes())) while not (is_valid_network(G, T_copy)): if iters >= 20: T_copy = T.copy() break T_copy = T.copy() # print(len(T_copy.nodes()), len(T.nodes())) to_remove = np.random.choice(T_copy.nodes(), n, replace=False) for x in to_remove: T_copy.remove_node(x) #count += 1 iters += 1 if not is_valid_network(G, T_copy): return find_tree(n - 1, T_copy, G) return T_copy
def random_prune(G, tree): min_tree = tree for k in range(1000): T = tree.copy() """list = range(25) random_range = random.choice(list) for j in range(70): #ramdom select leaves. if delete and valid, delete it temp = T.copy() leaves = [x for x in T.nodes() if T.degree(x) == 1 or T.degree(x) == 2] if len(leaves) == 0: break select = random.choice(leaves) temp.remove_node(select) if is_valid_network(G, temp): T = temp""" for j in range(30): temp = T.copy() for i in range(5): leaves = [x for x in T.nodes() if T.degree(x) == 1 or T.degree(x) == 2] if len(leaves) == 0: break select = random.choice(leaves) temp = T.copy() temp.remove_node(select) if is_valid_network(G, temp): T = temp if is_valid_network(G, temp) and average_pairwise_distance(temp) < average_pairwise_distance(T): T = temp if average_pairwise_distance(T) < average_pairwise_distance(min_tree): min_tree = T print(average_pairwise_distance(min_tree)) return min_tree
def add_edge(G, val): """ :param G: The original graph, used to check whether the new graph is a valid network. :param val: estimated cost of choosing each vertex. :return: possible best result """ best_graph = nx.Graph() edge_list = [] new_edge = "" node = val.index(max(val)) new_edge += str(node) max_val = -float("inf") max_neighbour = 0 for neighbour in G.neighbors(node): if val[neighbour] > max_val and neighbour != node: max_val = val[neighbour] max_neighbour = neighbour new_edge += " " + str(max_neighbour) new_edge += " " + str(G.get_edge_data(node, max_neighbour).get("weight")) edge_list.append(new_edge) new_graph = nx.parse_edgelist(edge_list, nodetype=int, data=(('weight', float), )) val[node], val[max_neighbour] = -float("inf"), -float("inf") if is_valid_network(G, new_graph): # if average_pairwise_distance_fast(new_graph) < best_so_far: # best_so_far = average_pairwise_distance_fast(new_graph) best_graph = copy.deepcopy(new_graph) return best_graph while True: new_edge = "" max_val = -float("inf") max_neighbour = 0 max_node = 0 check = False for node in new_graph.nodes(): for neighbour in G.neighbors(node): if val[neighbour] > max_val: max_val = val[neighbour] max_neighbour = neighbour max_node = node check = True if not check: break new_edge += str(max_node) new_edge += " " + str(max_neighbour) new_edge += " " + str( G.get_edge_data(max_node, max_neighbour).get("weight")) edge_list.append(new_edge) new_graph = nx.parse_edgelist(edge_list, nodetype=int, data=(('weight', float), )) val[max_node], val[max_neighbour] = -float("inf"), -float("inf") if is_valid_network(G, new_graph): best_graph = copy.deepcopy(new_graph) break return best_graph
def makeAllOutputFiles(): for file in os.listdir("inputs"): if file.endswith(".in"): print(os.path.join("inputs", file)) #input file input_path = os.path.join("inputs", file) G = read_input_file(input_path) T = solve(G) assert is_valid_network(G, T) #use brute force for small graphs if file.startswith("small") and len(T) > 2: print("Trying brute forcing on SMALL file: " + os.path.join("inputs", file)) #input file BRUTE_TREE = maes_second_dumbass_brute_force(G) if average_pairwise_distance_fast( BRUTE_TREE) <= average_pairwise_distance_fast(T): print("Small brute-force alg WINS.") T = BRUTE_TREE else: print("Solver alg WINS.") #nothing happens #print("Average pairwise distance: {}".format(average_pairwise_distance_fast(T))) outname = os.path.splitext(file)[0] + '.out' output_path = os.path.join("outputs", outname) print(output_path + "\n") write_output_file(T, output_path) assert validate_file(output_path) == True
def generate_random_old_way(self, s=1, d=0, high_degree=False): """ Returns a random valid starting state for local search. s: number of nodes to form T on d: minimum degree of each node on s """ nodes = list(self.graph.nodes) edges = list(self.graph.edges) self.network = nx.Graph() a = sorted(nodes, key=lambda n: len(self.graph.edges(n)), reverse=True) if high_degree: self.network.add_node(a[0]) else: self.network.add_node(nodes[0]) self.network = nx.minimum_spanning_tree(self.network) # print("Graph Size:", len(nodes)) s = random.randint(len(nodes) // 2, len(nodes)) # print("Subset Size:", s) while not is_valid_network(self.graph, self.network): # print("Looking") self.network.clear() T_nodes = random.sample(nodes, s) self.network.add_nodes_from(T_nodes) self.network.add_weighted_edges_from(self.relevant_edges(T_nodes)) self.network = nx.minimum_spanning_tree(self.network) s += 1
def remove(copy, G_copy, k): nonlocal min_dist nonlocal minT has_valid = False count = 0 for c in itertools.combinations(G_copy.edges(), k): temp = copy.copy() if count >= 10000: return solve(G) count += 1 for e in c: u, v = e[0], e[1] if copy.degree(u) == 1: copy.remove_node(u) elif copy.degree(v) == 1: copy.remove_node(v) else: copy.remove_edge(u, v) if is_valid_network(G, copy): has_valid = True if average_pairwise_distance_fast(copy) < min_dist: min_dist = average_pairwise_distance_fast(copy) minT = copy.copy() elif nx.is_dominating_set(G, copy.nodes()) and nx.is_connected(copy): has_valid = True copy = temp if not has_valid: return remove(copy, G_copy, k+1)
def trim_tree(G, T): """ Trims tree T as solution for graph G. Valid solutions for the problem are trees whose vertices are dominating sets. """ leaves = [l for l in T.nodes() if T.degree()[l] == 1] subTreeNodes = list(T.nodes()) cost = average_pairwise_distance_fast(T) while len(leaves) > 0: l, w = leaves[0], 100 for v in leaves: for u in T.adj[v]: if T.adj[v][u]['weight'] <= w: l, w = v, T.adj[v][u]['weight'] leaves.remove(l) toCheck = T.adj[l] subTreeNodes.remove(l) Tn = T.subgraph(subTreeNodes) if len(subTreeNodes) > 0 and is_valid_network(G, Tn): costn = average_pairwise_distance_fast(Tn) if costn < cost: leaves += [v for v in toCheck if Tn.degree[v] == 1] T = Tn cost = costn else: subTreeNodes.append(l) else: subTreeNodes.append(l) return T
def helper2(G): T = nx.minimum_spanning_tree(G) curr_lowest = average_pairwise_distance(T) curr_lowest_tree = T S = min_weighted_dominating_set(T) newG = nx.subgraph(T, S) ncc = nx.number_connected_components(newG) ccs = list(connected_components(newG)) for i in range(len(ccs) - 1): curr_node = ccs[i].pop() ccs[i].add(curr_node) next_node = ccs[i + 1].pop() ccs[i + 1].add(next_node) path = nx.dijkstra_path(G, curr_node, next_node) for n in path: if (n not in list(newG.nodes)): S.add(n) newG = nx.subgraph(G, S) newT = nx.minimum_spanning_tree(newG) if (is_valid_network(G, newT)): apd = average_pairwise_distance(newT) if (apd < curr_lowest): curr_lowest = apd curr_lowest_tree = newT return curr_lowest_tree
def solveFile(fileName: str, log = False) -> bool: """ Solve a graph saved in ./inputs/{fileName}.in and output it in output folder. Return if solve file succeed. """ try: G = read_input_file("./inputs/%s.in" % fileName) T = solver.ABC(G, N_EMPLOYED, N_ONLOOKER, N_ITERATIONS, FIRE_LIMIT, TERMINATION_LIMIT, log = log) assert(is_valid_network(G, T)) if os.path.exists("./outputs/%s.out" % fileName): oldT = read_output_file("./outputs/%s.out" % fileName, G) if len(T) == 1 and len(oldT) != 1: write_output_file(T, "./outputs/%s.out" % fileName) return True if len(oldT) == 1 or average_pairwise_distance(oldT) <= average_pairwise_distance(T): # do nothing print("File %s is skipped because old tree is better. " % fileName) return True write_output_file(T, "./outputs/%s.out" % fileName) return True except KeyboardInterrupt: raise KeyboardInterrupt except: # stdout print("ERROR: An error occured when processing on %s: %s" % (fileName, sys.exc_info()[0])) return False
def improve(G, temp, val): """ :param G: The original graph :param temp: Best graph we get so far. It's already a valid network, but in some cases add a new vertex can reduce the cost :param val: Heurisitics :return: new Best graph """ best_so_far = average_pairwise_distance_fast(temp) new_graph = copy.deepcopy(temp) while True: min_val = float("inf") min_neighbour = 0 min_node = 0 check = False for node in temp.nodes(): for neighbour in G.neighbors(node): if val[neighbour] < min_val: min_val = val[neighbour] min_neighbour = neighbour min_node = node min_weight = G.get_edge_data(min_node, min_neighbour).get("weight") new_graph.add_edge(min_node, min_neighbour, weight=min_weight) val[min_node], val[min_neighbour] = -1, -1 if is_valid_network(G, new_graph): if average_pairwise_distance_fast(new_graph) < best_so_far: best_so_far = average_pairwise_distance_fast(new_graph) check = True if not check: return temp return new_graph
def _start(self): """ Starting state for T. """ # self.network = nx.minimum_spanning_tree(self.graph) # self.generate_random_old_way(high_degree=True) self.generate_random_new_way() assert is_valid_network(self.graph, self.network)
def solve(G): """ Args: G: networkx.Graph Returns: T: networkx.Graph """ if G.number_of_nodes() <= 1: return G if G.number_of_nodes() == 2: G.remove_node(0) return G apd = [] T = naive_solve(G) if is_valid_network(G, T): apd.append(T) else: print('naive_solve not connected') T = greedy_mds(G) if is_valid_network(G, T): apd.append(T) else: print('greedy_mds not valid network') T = idea1(G, 'prune') if is_valid_network(G, T): apd.append(T) else: print('idea1 not valid network') T = idea2(G) if is_valid_network(G, T): apd.append(T) else: print('idea2 not valid network') T = idea3(G) if is_valid_network(G, T): apd.append(T) else: print('idea3 not valid network') return min(apd, key=average_pairwise_distance)
def random_weight(G, id): T = nx.Graph() nodes = list(G.nodes) v = np.random.choice(nodes, 1)[0] T.add_node(v) all_neighbors = set() count = 0 flag = True while not is_valid_network(G, T): #print(count) if flag: neighbors = [] for node in T: for w in G.neighbors(v): if w not in T: neighbors.append((node, w, 100 - G[v][w]['weight'])) for n in neighbors: all_neighbors.add(n) total = sum([weight for u, w, weight in all_neighbors]) dist = [weight / total for u, w, weight in all_neighbors] neighbor_edges = [(u, w, weight) for u, w, weight in all_neighbors] #print(dist) next_edge = neighbor_edges[np.random.choice(range(len(neighbor_edges)), p=dist)] T.add_edge(next_edge[0], next_edge[1], weight=next_edge[2]) if nx.is_tree(T): v = next_edge[1] flag = True else: T.remove_edge(next_edge[0], next_edge[1]) all_neighbors.remove(next_edge) flag = False count += 1 if T.number_of_nodes() == G.number_of_nodes(): print('no') break score = average_pairwise_distance_fast(T) T_copy = T.copy() for v in T: for w in G.neighbors(v): if w not in G: T_copy.add_edge(v, w, G.get_edge_data(v, w)['weight']) new_score = average_pairwise_distance_fast(T_copy) if new_score > score: T_copy.remove_edge(v, w) else: score = new_score print('good') #print(score - best_scores[id]) update_best_graph(T, id, 'random weight')
def algo2(G): """ add notes to previous algorithm. """ T = algo1(G) newT = addNodes(G, T, set(list(G.nodes)) - set(list(T.nodes))) newT = buildTree(G, list(newT.nodes)) assert is_valid_network(G, newT) return newT
def algo3(G): """ remove notes from previous algotirm. """ T = algo1(G.copy()) newT = removeNodes(G.copy(), T) newT = buildTree(G.copy(), list(newT.nodes)) assert is_valid_network(G, newT) return newT
def algo7(G): """ build tree in different way. """ T = algo6(G.copy()) allNodes = set(list(G.nodes)) for _ in range(1): T = addNodes(G, T, allNodes - set(list(T.nodes))) T = build(G, list(T.nodes)) T = removeNodes(G, T) T = build(G, list(T.nodes)) count = 3 while not is_valid_network(G, T) and count: T = addNodes(G, T, allNodes - set(list(T.nodes))) T = removeNodes(G, T) count -= 1 if not is_valid_network(G, T): return algo6(G.copy()) return T
def algo1(G): """ find the mdcs and connect them as components. Then find the min cost to connect components. """ domin = mwd(G) components = getComponents(G, domin) nodes = connectComponents(G, components) T = buildTree(G, nodes) assert is_valid_network(G, T) return T
def advanced_prunning(G, T): nodes = list(G.nodes) for i in nodes: temp = copy.deepcopy(T) temp2 = copy.deepcopy(temp) temp.remove_node(i) if (is_valid_network(G, temp)): T.remove_node(i) else: temp = copy.deepcopy(temp2) return T
def algo5(G): """ remove and node from mst. """ T = algo0(G.copy()) allNodes = set(list(G.nodes)) for _ in range(2): T = addNodes(G, T, allNodes - set(list(T.nodes))) T = removeNodes(G, T) assert is_valid_network(G, T) return T
def algo4(G): """ add and remove notes from tree. """ T = algo2(G) allNodes = set(list(G.nodes)) for _ in range(3): newT = addNodes(G, T, allNodes - set(list(T.nodes))) newT = removeNodes(G.copy(), newT) assert is_valid_network(G, newT) return newT
def heuristic_avg_cost(params): """ Loss function of average cost over training graphs. """ total_cost, count = 0, 0 for input in os.listdir('training/'): G = read_input_file('training/' + input) T = dijkstra_two_solve(G, [p1, p2, p3, p4]) assert is_valid_network(G, T) total_cost += average_pairwise_distance_fast(T) count += 1 return total_cost / count
def solve(G): """ Args: G: networkx.Graph Returns: solution tree """ T1 = solver.solve(G) T2 = solver1.solve(G) T3 = solver2.solve(G) if is_valid_network(G, T1) != True: return min([T2, T3], key=lambda x: average_pairwise_distance(x)) elif is_valid_network(G, T2) != True: return min([T1, T3], key=lambda x: average_pairwise_distance(x)) elif is_valid_network(G, T3) != True: return min([T1, T2], key=lambda x: average_pairwise_distance(x)) else: return min([T1, T2, T3], key=lambda x: average_pairwise_distance(x))
def prune_leaf(G, T): for _ in range(25): leaves = [x for x in T.nodes() if T.degree(x) == 1] for i in leaves: temp = T.copy() temp.remove_node(i) try: if is_valid_network(G, temp) and average_pairwise_distance(temp) < average_pairwise_distance(T): T = temp except: continue deg_two = [x for x in T.nodes() if T.degree(x) == 2] for i in deg_two: temp = T.copy() temp.remove_node(i) try: if is_valid_network(G, temp) and average_pairwise_distance(temp) < average_pairwise_distance(T): T = temp except: continue return T
def generate_new_solution(G, T): """With half probability decide to add or remove a vertex. If add a vertex, choose a vertex at random and add one of its neighbors that is not already within the graph. If remove vertex, choose a leaf at random, remove from tree, but only do so if the resulting graph is still a dominating set. Return the resulting solution tree.""" global adds, remove_v, switch_e roll = np.random.random() if (roll < 0.33): # add a vertex tree_vertices = list(T.nodes) rand_vertex = np.random.choice(tree_vertices) rand_neighbors = list(G.adj[rand_vertex]) rand_neighbors = [ rn for rn in rand_neighbors if rn not in tree_vertices ] # ensure candidate neighbors not already inside the tree if not rand_neighbors: # empty list return generate_new_solution(G, T) else: adds += 1 chosen = np.random.choice(rand_neighbors) T.add_node(chosen) T.add_edge(chosen, rand_vertex, weight=G.get_edge_data(chosen, rand_vertex)['weight']) return T elif (roll < 0.67): # remove a leaf tree_vertices = list(T.nodes) T_leaves = [l for l in tree_vertices if T.degree(l) == 1] np.random.shuffle(T_leaves) for leaf in T_leaves: T_prime = T.copy() T_prime.remove_node(leaf) if is_valid_network(G, T_prime): remove_v += 1 return T_prime return generate_new_solution(G, T) else: # switch an edge switch_e += 1 tree_edges = list(T.edges) rand_index = np.random.choice(len(tree_edges)) rand_edge = tree_edges[rand_index] T.remove_edge(rand_edge[0], rand_edge[1]) cc = list(nx.connected_components(T)) # randomly adding back in an edge to reconnect the two CCs for vertex_a in cc[0]: for vertex_b in cc[1]: if G.has_edge(vertex_a, vertex_b): T.add_edge(vertex_a, vertex_b, weight=G.get_edge_data(vertex_a, vertex_b)['weight']) return T
def algo6(G): """ remove and add nodes from previous tree. """ T = algo5(G.copy()) T = removeNodes(G, T) count = 3 allNode = set(list(G.nodes)) while not is_valid_network(G, T) and count: T = addNodes(G, T, allNode - set(list(T.nodes))) T = removeNodes(G, T) count -= 1 return T
def update_best_graph(G, id, method): def write_best_graph(): filename = id.split('.')[0] write_output_file(G, OUTPUT_PATH + filename + '.out') print(method + ' ' + id) if not is_valid_network(inputs[id], G): print("ERROR: " + id + ' ' + method + ' ') #print(nx.is_tree(inputs[id])) if G.number_of_nodes() == 1: # put this in finished files if id not in finished_files: write_best_graph() best_scores[id] = 0 finished_files.add(id) best_methods[id] = method else: current_score = average_pairwise_distance_fast(G) if current_score < best_scores[id] and is_valid_network(inputs[id], G): write_best_graph() best_scores[id] = current_score best_methods[id] = method
def makeAllOutputFiles(): for file in os.listdir("inputs"): if file.endswith(".in"): print(os.path.join("inputs", file)) #input file input_path = os.path.join("inputs", file) G = read_input_file(input_path) try: T = solve(G) except: print("ERRORED OUT. CONTINUE ANYWAY") T = G assert is_valid_network(G, T) #randomization optimization if len(T) > 2: print("Trying randomization to find better result..") try: betterT = maes_randomization_alg( G, T, 100) #50 iterations of randomness except: print("ERRORED OUT. CONTINUE ANYWAY") betterT = G assert is_valid_network(G, betterT) if average_pairwise_distance_fast( betterT) < average_pairwise_distance_fast(T): print("BETTER TREE FOUND.") T = betterT else: print("No improvements.") #nothing happens #print("Average pairwise distance: {}".format(average_pairwise_distance_fast(T))) outname = os.path.splitext(file)[0] + '.out' output_path = os.path.join("outputs", outname) print(output_path + "\n") write_output_file(T, output_path) assert validate_file(output_path) == True
def generate_random_new_way(self): og = min_weighted_dominating_set(self.graph, "weight") start_node = og.pop() holder = set() while og: curr_node = og.pop() holder.update(dijkstra_path(self.graph, start_node, curr_node)) if holder and is_valid_network(self.graph, self.graph.subgraph(holder)): self.network = nx.minimum_spanning_tree( self.graph.subgraph(holder)) else: self.generate_random_old_way(high_degree=True)
def random_edges(G, id): T = nx.Graph() edges = list(np.random.permutation(G.edges)) for edge in edges: T.add_edge(edge[0], edge[1], weight=G.get_edge_data(edge[0], edge[1])['weight']) if not nx.is_tree(T): T.remove_edge(edge[0], edge[1]) if is_valid_network(G, T): score = average_pairwise_distance_fast(T) print(score - best_scores[id]) update_best_graph(T, id, 'random edges') break