def solve(G): """ Args: G: networkx.Graph Returns: T: networkx.Graph """ g = GraphSolver(G) start = g.find_leaf_path() T = g.dijkstra_solve_graph(start, calculate_heuristic, first_heuristic) optimize_sorted(g, T) rebuilder = Rebuilder(g) min_T = T.copy() min_cost = average_pairwise_distance(T) # print('*', min_cost) for _ in range(50): if rebuilder.rebuild(): g = GraphSolver(G) for v in min_T.nodes: g.visit(v) for e in min_T.edges: g.add_edge(e) # print('reset') g.dijkstra_solve_graph(start, calculate_heuristic, first_heuristic) optimize_sorted(g, g.T) cost = average_pairwise_distance(g.T) # print(cost) if cost < min_cost: min_cost = cost min_T = g.T.copy() return min_T
def solve(G): """ Args: G: networkx.Graph Returns: T: networkx.Graph """ t = most_between(G) solver = GraphSolver(G) for node in t.nodes: solver.visit(node) for edge in t.edges: solver.add_edge(edge) optimize_sorted(solver, solver.T, kill_cycle_all_paths) return solver.T
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 optimize_removal(solver: GraphSolver, tree: Graph, orig_cost: float): candidates = [ v for v in tree.nodes if not solver.is_required(v) and is_leaf(tree, v) ] while candidates: # print(candidates) # random.seed(0) node = random.choice(candidates) candidates.remove(node) edge = (node, list(tree.neighbors(node))[0]) solver.unvisit(node) new_cost = average_pairwise_distance(tree) if new_cost < orig_cost: # print('removed', node) return optimize_removal(solver, tree, new_cost) else: solver.add_edge(edge) return orig_cost
def solve(G): """ Args: G: networkx.Graph Returns: T: networkx.Graph """ t = find_shortest_paths(G) solver = GraphSolver(G) for node in t.nodes: solver.visit(node) for edge in t.edges: solver.add_edge(edge) optimize_sorted(solver, solver.T, kill_cycle_all_paths) check = is_valid_network(G, solver.T) if not check: raise Exception('invalid') return solver.T
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_removal_sorted(solver: GraphSolver, tree: Graph, orig_cost: float): candidates = [ v for v in tree.nodes if not solver.is_required(v) and is_leaf(tree, v) ] while candidates: # print(candidates) # random.seed(0) candidates.sort(key=lambda x: closeness_centrality(solver.T, x)) node = candidates.pop(0) edge = (node, list(tree.neighbors(node))[0]) solver.unvisit(node) new_cost = average_pairwise_distance(tree) if new_cost < orig_cost: # print('removed', node) return optimize_removal_sorted(solver, tree, new_cost) else: solver.add_edge(edge) return 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