def solve(G, T=None, cost=float('inf'), multiplier=1): """ Args: G: networkx.Graph T: the best known dominating-set tree. None if unknown cost: the cost of the best known T Returns: T: networkx.Graph """ tries, max_tries = 0, int(multiplier * len(list(G.nodes))) while tries < max_tries: if cost == 0: break nodes = list(G.nodes) random.shuffle(nodes) i = 0 while not nx.is_dominating_set(G, nodes[:i]) or not nx.is_connected( G.subgraph(nodes[:i])): i += 1 new_T, new_cost = min( (pick_leaves(G, t, utils.average_pairwise_distance_fast(t)) for t in gen_candidates(G, nodes[:i])), key=lambda p: p[1]) if new_cost < cost: T, cost = new_T, new_cost tries = 0 else: tries += 1 return T, cost
def solve2(G): """ Args: G: networkx.Graph Returns: T: networkx.Graph """ pq = [] #min heap for Kruskals edge popping for e in G.edges: heappush(pq, (-G.edges[e]['weight'], e)) T = copy.deepcopy(G) costpq = [] #min heap with minimal pairwise distance with its tree while pq: if (T.number_of_nodes() == 2): break node = heappop(pq) e = node[1] # print(e) w = node[0] * -1 T.remove_edge(e[0], e[1]) if T.degree(e[1]) == 0: T.remove_node(e[1]) if T.degree(e[0]) == 0: T.remove_node(e[0]) if nx.is_connected(T) and nx.is_dominating_set(G, T): if nx.is_tree(T): heappush(costpq, (average_pairwise_distance_fast(T), T)) # cost = average_pairwise_distance_fast(T) else: T.add_edge(e[0], e[1], weight=w) return heappop(costpq)[1]
def combine(folders): inputs = {} best_scores = {} best_outputs = {} for filename in os.listdir(INPUT_PATH): inputs[filename] = read_input_file(INPUT_PATH + filename) for folder in folders: for filename in os.listdir(folder): if filename != '.DS_Store': output_graph = read_output_file(folder + filename, inputs[filename.split('.')[0] + '.in']) if output_graph.number_of_nodes() == 1: best_scores[filename] = 0 best_outputs[filename] = output_graph else: score = average_pairwise_distance_fast(output_graph) if filename not in best_scores: best_scores[filename] = score best_outputs[filename] = output_graph elif filename in best_scores and score < best_scores[filename]: best_scores[filename] = score best_outputs[filename] = output_graph for id in best_outputs: write_output_file(best_outputs[id], OUTPUT_PATH + id)
def pruneLeavesDesc(l, res, currentAvg): #VERY naive solution bc we repeat avg_pairwise_distance method on EVERY call leaves = l.values() #print(leaves) removedLeaves = [] temp = res.copy() verticesRemoved = {} #start with the largest edge weight of leaves first for elem in reverse(leaves): temp.remove(elem) print(temp) #create new graph to recalculate avg pairwise distance w/o elem Tr = nx.Graph() Tr.add_weighted_edges_from(temp) new_avg = average_pairwise_distance_fast(Tr) #if better avg obtained w/o elem: remove it and update avg #else, add it back and move on to next leaf if new_avg <= currentAvg: removedLeaves.append(elem) verticesRemoved.add([elem[0], elem[1]]) #add vertices to this set currentAvg = new_avg print("removed:"+ str(new_avg)) else: temp.add(elem) print("kept:"+ str(new_avg)) return (removedLeaves, verticesRemoved)
def small_graph_bruteforce_recursive(G, currG, costPQ): T = copy.deepcopy(currG) #for all edges in the current graph for e in T.edges: #BASE CASE: continue or don't continue if nx.is_connected(currG) and nx.is_dominating_set(G, currG): if nx.is_tree(currG): heappush( costPQ, (average_pairwise_distance_fast(currG), currG)) #records print(costPQ[0]) else: #the current graph is not connected or dominating return e0 = e[0] e1 = e[1] w = G.get_edge_data(e0, e1)['weight'] #remove then recursive currG.remove_edge(e[0], e[1]) if currG.degree(e[1]) == 0: currG.remove_node(e[1]) if currG.degree(e[0]) == 0: currG.remove_node(e[0]) small_graph_bruteforce_recursive(G, currG, costPQ) #recursive call #re-add what I just removed if e0 not in currG.nodes: currG.add_node(e0) if e1 not in currG.nodes: currG.add_node(e1) currG.add_edge(e0, e1, weight=w)
def solve(G): """ Args: G: networkx.Graph Returns: T: networkx.Graph """ min_tree = nx.Graph() min_cost = float('inf') # if by luck there's a node connecting to all other verts, return it for node in G.nodes: if len(list(nx.neighbors(G, node))) == nx.number_of_nodes(G) - 1: min_tree.add_node(node) return min_tree start_vertices = starting_point(G) # run greedy alg on all starting vertex candidates for start_vertex in start_vertices: curr_tree = greedy_find_min_tree(G, start_vertex) curr_cost = average_pairwise_distance_fast(curr_tree) if curr_cost < min_cost: min_cost = curr_cost min_tree = curr_tree return min_tree
def solve(G): """Approximates a connected dominating set across a graph input by selecting the optimal solution from many randomly generated candidate solutions. Parameters ---------- G: NetworkX graph Returns ------- T: A connected dominating set across G Notes ----- This function was designed to be a randomized approximation algorithm. This is an NP-hard problem which makes finding a correct solution computationally difficult. However, by employing probabilistic measures, this algorithm can ensure a solution T that approximates the optimal solution T'. """ # initialize variables to track, and eventually return, best tree best_distance = float('inf') best_tree = G curr_tree = G # run loop 1000 times for each graph input to continually improve solution over time for i in range(1000): curr_tree = solve_computation(G) curr_distance = average_pairwise_distance_fast(curr_tree) if (curr_distance < best_distance): best_distance = curr_distance best_tree = curr_tree return best_tree
def pruneLeavesAll(l, res, currentAvg, totalVs): prunedEdges = l.values() prunedVertices = l.keys() Tr = nx.Graph() Tr.add_weighted_edges_from([i for i in res if i not in prunedEdges]) Tr.add_nodes_from([i for i in totalVs if i not in prunedVertices]) avgAll = average_pairwise_distance_fast(Tr) return (prunedEdges, prunedVertices, avgAll)
def heuristic(edge): if edge[0] == edge[1]: return float('inf') T_copy = nx.Graph(T) T_copy.add_edge(edge[0], edge[1], weight=edge[2].get('weight', 1)) T_copy.add_node(edge[0]) T_copy.add_node(edge[1]) if not nx.is_tree(T_copy): return float('inf') return average_pairwise_distance_fast(T_copy)
def brute_force(G, id): edges = list(G.edges) for i in range(1, len(edges)): print(i) lst = list(combinations(edges, i)) print(len(lst)) for edge_group in lst: T = nx.Graph() for edge in edge_group: T.add_edge(edge[0], edge[1], weight=G.get_edge_data(edge[0], edge[1])['weight']) if is_valid_network( G, T) and average_pairwise_distance_fast(T) < best_scores[id]: print(average_pairwise_distance_fast(T)) update_best_graph(T, id, 'brute_force')
def dijkstra_two_solve(G, params=DEFAULT_PARAMS, k=2, all_trees=False): """ Wrapper method that generats solutions using dijkstra_two Args: G: nx.Graph() params: allows Dijkstras to account for several properties of edges k: tries upto k source vertices if k distinct vertices exist all_trees: Flags whether to return best tree or all of them Returns: If all_trees, then it returns an array of all the generated trees. Else, it returns the single best shortest path tree. """ n = len(G.nodes()) k = min(n, k) #Trivial cases that otherwise error if n == 1: return G elif n == 2: T = nx.Graph() T.add_node(0) return T #Uses dijkstras to find metric closure distances D = [[0 for u in G.nodes()] for v in G.nodes()] all_paths_iter = nx.all_pairs_dijkstra(G) for u, (distance, path) in all_paths_iter: for v in distance: D[u][v] = distance[v] avgD = [mean(d) for d in D] starts = sorted(G.nodes(), key=lambda v: avgD[v])[0:k] if all_trees: Trees = [] for s in starts: T = dijkstra_two(G, s, D=D, params=params) cost = average_pairwise_distance_fast(T) Trees.append(T) return Trees else: minval, mintree = 1000000, None for s in starts: T = dijkstra_two(G, s, D=D, params=params) cost = average_pairwise_distance_fast(T) if cost < minval: minval = cost mintree = T return mintree
def scorer(filename): input = INPUT_PATH + filename print(input) out_filename = filename.split('.')[0] + '.out' print(OUTPUT_PATH + out_filename) if os.path.isfile(OUTPUT_PATH + out_filename): output_graph = read_output_file(OUTPUT_PATH + out_filename, read_input_file(input)) print(average_pairwise_distance_fast(output_graph))
def remove_leaves(G, tree): all_leaves = [] new_leaves = find_new_leaves(tree, [], all_leaves) for leaf in new_leaves: cost_with_leaf = average_pairwise_distance_fast(tree) for neighbor in tree.neighbors(leaf): neighbor_leaf = neighbor tree.remove_node(leaf) cost_without_leaf = average_pairwise_distance_fast(tree) if (nx.is_dominating_set(G, tree.nodes())): if cost_with_leaf <= cost_without_leaf: edge_weight = G[neighbor_leaf][leaf]['weight'] tree.add_edge(neighbor_leaf, leaf, weight=edge_weight) else: edge_weight = G[neighbor_leaf][leaf]['weight'] tree.add_edge(leaf, neighbor_leaf, weight=edge_weight) new_leaves.remove(leaf) new_leaves = find_new_leaves(tree, new_leaves, all_leaves) return tree
def solveTree(G): """ solve for tree - evaluating whether a leaf is worth adding to T """ nonleaf = [v for v in G.nodes() if G.degree[v] > 1] T = G.subgraph(nonleaf).copy() avg = average_pairwise_distance_fast(T) leafs = [v for v in G.nodes() if G.degree[v] == 1] for v in leafs: u = G.neighbors(v) weight = G.get_edge_data(v, u, default=0) if weight < avg and weight != 0: T.add_node(v) tempAvg = average_pairwise_distance_fast(T) if tempAvg > avgv: T.remove_node(v) else: avg = tempAvg return T
def simulatedAnnealing(G): vertices = list(G.nodes) edges = list(G.edges.data('weight')) #generating random start vertex v = vertices[random.randint(0,len(vertices) - 1)] #starting our current tree with start vertex solution_T = nx.Graph() solution_T.add_node(v) curr_pairwise_dist = float("inf") totalTimeSteps = 10000 # timesteps > 0 or until we've added all the vertices to the tree while (totalTimeSteps > 0 or len(list(solution_T.nodes)) < len(vertices)): # getting the minimum outgoing edge #outgoing_Edges = {4: {'weight': 25.643}, 1: {'weight': 54.048}} outgoing_edges = [(n, nbrdict) for n, nbrdict in G.adjacency() if n == v][0][1] degree_One = () #outgoing_Edges = {(0,4): 25.643, (0,1): 54.048} outgoing_edges = {(v,x):y['weight'] for x,y in outgoing_edges.items()} #min_edge = (0,4) min_edge = min(outgoing_edges.keys(), key=(lambda k: outgoing_edges[k])) #(min_edge, outgoing_edges[min_edge]) = ((0,4), 25) min_edge = (min_edge, outgoing_edges[min_edge]) # end vertex of min edge u = min_edge[0][1] temp = solution_T.copy() print(totalTimeSteps) print(list(temp.edges)) temp.add_node(u) temp.add_edge(v, u, weight=min_edge[1]) print(list(temp.edges)) temp_pairwise_distance = average_pairwise_distance_fast(temp) if(temp_pairwise_distance < curr_pairwise_dist or ): try: nx.find_cycle(temp) except: solution_T.add_node(u) solution_T.add_edge(v, u, weight=min_edge[1]) v = u curr_pairwise_dist = temp_pairwise_distance elif(math.exp((curr_pairwise_dist - temp_pairwise_distance) / totalTimeSteps) < random.uniform(0,1)): try: nx.find_cycle(temp) except: solution_T.add_node(u) solution_T.add_edge(v, u, weight=min_edge[1]) v = u curr_pairwise_dist = temp_pairwise_distance totalTimeSteps *= 1-(0.003); return solution_T
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 removeLeaves2(leaves, MST, G): """Remove leaves in LEAVES from an MST until MST is no longer a dominating set of G AND the average pairwise distance is decreased.""" num_removed_leaves = 0 impt_vertices = [] for leaf in leaves: if (leaf and list(MST.edges(leaf, data='weight'))): e = list(MST.edges(leaf, data='weight'))[0] cost = average_pairwise_distance_fast(MST) #Test removing an edge and finding new avg pairwise dist. MST.remove_node(leaf) new_cost = average_pairwise_distance_fast(MST) if (new_cost > cost or not nx.is_dominating_set(G, MST.nodes)): #Put the leaf node and edge back. MST.add_node(leaf) MST.add_edge(e[0], e[1], weight=e[2]) #print("Leaf kept") impt_vertices.append(leaf) else: num_removed_leaves += 1 return MST, impt_vertices, num_removed_leaves
def prune_leaves_cost(leaves, min_tree): removed = 0 kept_leaves = [] for leaf in leaves: if (leaf): e = list(min_tree.edges(leaf, data='weight'))[0] #print("leaf: " + str(leaf)) #print("leaf edge: " + str(e)) cost = average_pairwise_distance_fast(min_tree) min_tree.remove_node(leaf) new_cost = average_pairwise_distance_fast(min_tree) if (new_cost > cost or not nx.is_dominating_set(G, min_tree.nodes)): min_tree.add_node(leaf) min_tree.add_edge(e[0], e[1], weight=e[2]) #print("Leaf kept") kept_leaves.append(leaf) else: removed += 1 #if (removed): #print("(Cost) We removed " + str(removed) + " leaves in this iter") return min_tree, kept_leaves, removed
def solve(G): """ Args: G: networkx.Graph Returns: T: networkx.Graph """ pq = [] for e in G.edges: heappush(pq, (-G.edges[e]['weight'], e)) T = copy.deepcopy(G) print(T == G) print(type(T)) costpq = [] while pq: node = heappop(pq) e = node[1] print(e) w = node[0] T.remove_edge(e[0], e[1]) if T.degree(e[1]) == 0: T.remove_node(e[1]) if T.degree(e[0]) == 0: T.remove_node(e[0]) # print("CONNECT") # print() if nx.is_connected(T) and nx.is_dominating_set(G, T): print("SDFSDF") if nx.is_tree(T): heappush(costpq, (average_pairwise_distance_fast(T), T)) cost = average_pairwise_distance_fast(T) else: T.add_edge(e[0], e[1], weight=w) # return 0 return heappop(costpq)[0]
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 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
def helper(G): T = None if G.size() == len(G) - 1: all_nodes = G.nodes() T = G.copy() for n in all_nodes(): cost = average_pairwise_distance_fast(G) if G.degree(n) == 1: original_graph = T.copy() T.remove_node(n) temp_cost = average_pairwise_distance_fast(T) if temp_cost > cost: T = original_graph else: cost = temp_cost el = list(G.edges) el.sort() if el == [(0, 2), (1, 3), (1, 4), (2, 1), (2, 3)]: T = G.copy() T.remove_node(0) T.remove_node(4) T.remove_node(3) # all_nodes = G.nodes # for n in all_nodes(): # cost = average_pairwise_distance_fast(G) # if G.degree(n) == 1 or G.degree(n) == 2: # original_graph = T.copy() # T.remove_node(n) # temp_cost =average_pairwise_distance_fast(T) # if temp_cost > cost: # T = original_graph # else: # cost = temp_cost if T is not None and is_valid_network(G, T): return T else: return None
def prune(T, G, leaves): score = average_pairwise_distance_fast(T) while leaves: l = leaves.popleft() parent = list(T.neighbors(l))[0] edge_weight = T.get_edge_data(parent, l)['weight'] T.remove_node(l) if nx.is_dominating_set(G, T.nodes): new_score = average_pairwise_distance_fast(T) p = np.random.random() if new_score > score and p > 0.5: T.add_node(l) T.add_edge(l, parent, weight=edge_weight) else: score = new_score #append new leaves if T.degree(parent) == 1: leaves.append(parent) else: T.add_node(l) T.add_edge(l, parent, weight=edge_weight) return T
def _search(self, steps): """ Performs one iteration of local search and returns a possible T. """ self._start() transitions = 0 for i in range(steps): neighbors = [ n for n in self._neighbors() if len(n.nodes) > 0 and is_valid_network(self.graph, n) ] if neighbors == []: continue else: neighbor = min(neighbors, key=average_pairwise_distance_fast) # If the neighbor is invalid, ignore it. if neighbor.nodes and is_valid_network(self.graph, neighbor): f = average_pairwise_distance_fast(self.network) f_p = average_pairwise_distance_fast(neighbor) delta = f_p - f prob = self._prob_sched(transitions + 1, delta) # Transition? if delta < 0: # print(f_p) transitions += 1 self.network = neighbor elif np.random.random() <= prob: # print(f_p, prob) transitions += 1 self.network = neighbor return self.network
def heuristics(G): """ :param G: Given a graph G :return: the estimated cost of choosing each vertex. """ res = [] degree = G.degree() deg_para = 10000 / G.number_of_edges() wei_para = -100 / average_pairwise_distance_fast(G) for i in range(0, G.number_of_nodes()): total_weight = 0 for neighbour in G.neighbors(i): total_weight += G.get_edge_data(i, neighbour).get('weight') res.append(deg_para * degree[i] + wei_para * total_weight) return res
def scout(self, init: bool = False) -> None: # has some chance to use best SPT if not init and random.random() < SPT_RATE: init = True self.solution = randomDominatingTree(self.G, init=init) self.unimprovedTimes = 0 if len(self.solution) == 1: self.currentCost = 0 else: self.currentCost = average_pairwise_distance_fast(self.solution) self.leaves = [] # find leaves in the tree for v in self.solution.nodes: if len(self.solution[v]) == 1: self.leaves.append(v)
def solve(G): """ Args: G: networkx.Graph Returns: T: networkx.Graph + the algorithm used to produce it. """ # Base Case: If no edges, we return the graph itself. if len(G.edges()) == 0: return G, None, 0 #Algorithm Candidate 1: Cut ALL Leaf Nodes off MST. pruned_MST1 = alg1(G) #Algorithm Candidate 2: Cut ALL Leaf Nodes off MST by edge weight consideration. pruned_MST2 = alg2(G) # Algorithm Candidate 3: Dominating Set Weight3 DS_MST3 = alg3(G, 3) # Algorithm Candidate 4: Dominating Set Weight1 DS_MST1 = alg3(G, 1) # Algorithm Candidate 5: Dominating Set Weight2 DS_MST2 = alg3(G, 2) # # Algorithm Candidate 5: Dominating Set Weight0 # DS_MST0 = alg3(G, 0) DS_MST4 = alg4(G) # Print out results: Attach name of algorithm with distance and the subgraph. if DS_MST1: dsMST1 = ["Dominating Set MST 1", average_pairwise_distance_fast(DS_MST1), DS_MST1] else: dsMST1 = ['none', float('inf'), None] if DS_MST2: dsMST2 = ["Dominating Set MST 2", average_pairwise_distance_fast(DS_MST2), DS_MST2] else: dsMST2 = ['none', float('inf'), None] if DS_MST3: dsMST3 = ["Dominating Set MST 3", average_pairwise_distance_fast(DS_MST3), DS_MST3] else: dsMST3 = ['none', float('inf'), None] if DS_MST4: dsMST4 = ["Dominating Set MST 4", average_pairwise_distance_fast(DS_MST4), DS_MST4] else: dsMST4 = ['none', float('inf'), None] mst1 = ["Pruned MST 1", average_pairwise_distance_fast(pruned_MST1), pruned_MST1] mst2 = ["Pruned MST 2", average_pairwise_distance_fast(pruned_MST2), pruned_MST2] best_algorithm = min([dsMST1, dsMST2, dsMST3, dsMST4, mst1, mst2], key=lambda x: x[1])#Find best algorithm by minkey on avg pairwise dist. best_alg_name = best_algorithm[0] best_alg_avg_dist = best_algorithm[1] best_alg_avg_subgraph = best_algorithm[2] #print(best_alg_name + 'optimal') #Return optimal subgraph + name of producing algorithm return best_alg_avg_subgraph, best_alg_name, best_alg_avg_dist
def greedy(G, weight='weight'): from random import choice T = G.__class__() if len(G) == 1: return G node = choice(list(G.nodes())) next_edges = list(G.edges(node, data=True)) T.add_node(node) def heuristic(edge): if edge[0] == edge[1]: return float('inf') T_copy = nx.Graph(T) T_copy.add_edge(edge[0], edge[1], weight=edge[2].get('weight', 1)) T_copy.add_node(edge[0]) T_copy.add_node(edge[1]) if not nx.is_tree(T_copy): return float('inf') return average_pairwise_distance_fast(T_copy) while True: curr_avg = average_pairwise_distance_fast(T) min_edge = min(next_edges, key=heuristic) min_new_avg = heuristic(min_edge) if is_valid_network(G, T) and min_new_avg > curr_avg: break else: if T.has_node(min_edge[0]): T.add_node(min_edge[1]) T.add_edge(min_edge[0], min_edge[1], weight=min_edge[2].get('weight', 1)) next_edges = next_edges + (list(G.edges(min_edge[1], data=True))) elif T.has_node(min_edge[1]): T.add_node(min_edge[0]) T.add_edge(min_edge[0], min_edge[1], weight=min_edge[2].get('weight', 1)) next_edges = next_edges + (list(G.edges(min_edge[0], data=True))) next_edges = list( filter( lambda e: not (e[0] == min_edge[0] and e[1] == min_edge[1]) and not (e[0] == min_edge[1] and e[1] == min_edge[0]), next_edges)) return T
def pick_leaves_helper(G, T, edges, i): if not utils.is_valid_network(G, T): return float("inf"), [] leafset = set(leaf for leaf, neighbor in edges) new_leaf_edges = [(n, list(T[n])[0]) for n in T.nodes if n not in leafset and len(T[n]) == 1] edges = edges + new_leaf_edges cost, nodes_to_remove = utils.average_pairwise_distance_fast(T), [] for k in range(i, len(edges)): leaf, neighbor = edges[k] w = T[leaf][neighbor]['weight'] T.remove_node(leaf) new_cost, additional_nodes = pick_leaves_helper(G, T, edges, k + 1) if new_cost < cost: cost, nodes_to_remove = new_cost, [leaf] + additional_nodes T.add_edge(leaf, neighbor, weight=w) return cost, nodes_to_remove
def solve(G): """ Args: G: networkx.Graph Returns: T: networkx.Graph """ #hard code methods #greedy2 #greedy2_more_edges (only small and median) #trimMST #ds_spt #compute the cost only if not none min_cost = float('inf') min_tree = None tree_greedy1 = solver_greedy1.solve(G) if average_pairwise_distance_fast(tree_greedy1) < min_cost: min_cost = average_pairwise_distance_fast(tree_greedy1) min_tree = tree_greedy1 if (nx.number_of_nodes(G) == 25 or nx.number_of_nodes(G) == 50): tree_greedy2 = solver_greedy2.solve(G) if average_pairwise_distance_fast(tree_greedy2) < min_cost: min_cost = average_pairwise_distance_fast(tree_greedy2) min_tree = tree_greedy2 tree_mst = solver_mst.solve(G) if average_pairwise_distance_fast(tree_mst) < min_cost: min_cost = average_pairwise_distance_fast(tree_mst) min_tree = tree_mst tree_spt = solver_spt.solve(G) if average_pairwise_distance_fast(tree_spt) < min_cost: min_cost = average_pairwise_distance_fast(tree_spt) min_tree = tree_spt return min_tree