def kill_cycle(solver: GraphSolver, cycle: list, orig_cost: float): """ Returns the first edge found in the cycle which, if removed from tree, leads to a decrease in cost (average pairwise distance). :param tree: tree (with 1 extra edge) to consider :param cycle: list of edges to consider (which form a cycle, removal of any restores tree) :param orig_cost: original cost :return: """ tree = solver.T for edge in cycle: solver.remove_edge(edge) new_cost = average_pairwise_distance(tree) still_valid = is_valid_network(solver.G, solver.T) solver.add_edge(edge) if new_cost < orig_cost and still_valid: return edge, new_cost return None, orig_cost
def kill_cycle_all_paths(solver: GraphSolver, cycle: list, orig_cost: float): """ Returns the first edge found in the cycle which, if removed from tree, leads to a decrease in cost (average pairwise distance). :param tree: tree (with 1 extra edge) to consider :param cycle: list of edges to consider (which form a cycle, removal of any restores tree) :param orig_cost: original cost :return: """ tree = solver.T weights = [0] * len(cycle) # Count the number of times each edge in the cycle appears in the paths for n, (dist, path) in all_pairs_dijkstra(solver.G): for target in path.keys(): for v in range(1, len(path[target])): path_edge = (path[target][v - 1], path[target][v]) if path_edge in cycle: weights[cycle.index(path_edge)] += 1 # Multiply by the edge weight to get the contributing weight weights = [ weights[i] * weight(solver.G, cycle[i]) for i in range(len(weights)) ] edge = cycle[argmin(weights)] # print('weights for', cycle, 'are', weights) # cycle.sort(key= lambda x: weight(solver.T, x), reverse = True) # edge = cycle[0] solver.remove_edge(edge) new_cost = average_pairwise_distance(tree) solver.add_edge(edge) if new_cost < orig_cost: # print('nice') return edge, new_cost else: # print('removing', edge, 'from cycle', cycle, "didn't decrease cost because", new_cost, '>=', orig_cost) # print(weight(solver.T, edge), 'from', [weight(solver.T, e) for e in cycle]) pass return None, orig_cost
def optimize_additions(solver: GraphSolver, tree: Graph, orig_cost: float = None): graph = solver.G if orig_cost is None: orig_cost = average_pairwise_distance(tree) # get all edges to consider edges = [] for node in tree.nodes: for neighbor in graph.neighbors(node): if not edge_exists(tree.edges, (node, neighbor)) and not edge_exists( edges, (node, neighbor)): edges.append((node, neighbor)) # for each edge (consider order randomly) while edges: # random.seed(0) added_edge = random.choice(edges) edges.remove(added_edge) weight = graph[added_edge[0]][added_edge[1]]['weight'] # if added edge creates a cycle if added_edge[1] in tree.nodes: solver.add_edge(added_edge) while (True): try: cycle: list = find_cycle(tree, added_edge[0]) except: # No cycle break try: cycle.remove(added_edge) except ValueError: cycle.remove(added_edge[::-1]) replaced_edge, new_cost = kill_cycle(solver, cycle, orig_cost) # print("replaced_edge:", replaced_edge) # print("cost:", new_cost) if replaced_edge: orig_cost = new_cost solver.remove_edge(replaced_edge) else: solver.remove_edge(added_edge) # if other vertex not in tree else: v = added_edge[1] solver.visit(v, added_edge) # add_edge(tree, added_edge, weight) new_cost = average_pairwise_distance(tree) if new_cost < orig_cost: orig_cost = new_cost add_neighbors(solver, edges, v) else: solver.unvisit(v) # remove considered edge return orig_cost