def Ai(self, k, r, X, i): T = SuitabilityGraph() while k > 0: TBEST = SuitabilityGraph() for kprime in range(1, k + 1): for v in self.__nodes: if i > 1: Tprime = self.Ai(kprime, v, X, i - 1) p = self.__paths[tuple(sorted([r, v]))] Tprime.append_path(p, self.__graph) else: dists = {} for t in self.__terminals: dists[t] = self.__dist[tuple(sorted([v, t]))] ord_term = sorted(dists.iteritems(), key=operator.itemgetter(1)) Tprime = SuitabilityGraph() for j in range(kprime): p = self.__paths[tuple(sorted([v, ord_term[j][0]]))] Tprime.append_path(p, self.__graph) if self.d(TBEST) > self.d(Tprime): TBEST = Tprime T.append_graph(TBEST) k -= len(set(TBEST.keys()).intersection(X)) X = set(X).difference(TBEST.keys()) return T
class Baltz: def __init__(self, graph): # Init some instance variables. self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) self.__edges = graph.get_edges() self.__congestion = {} l_G = float(0) for e, le in self.__edges.iteritems(): self.__congestion[e] = 0 l_G += le self.__costs = {e: le / l_G for e, le in self.__edges.iteritems()} def steiner_forest(self, requests, k=4): lambda_ = 1 MSTs = {} n = len(self.__graph) for i, r in enumerate(requests): self.__graph.update_edge_weights({ e: self.__costs[e] * n**(self.__congestion[e] / lambda_ - 1) for e in self.__edges }) mz = VST_RS(self.__graph) Ti, l, _, _, _, _, _ = mz.steiner_forest(r[1:], [r[0]], k, sys.maxint) MSTs[i] = (Ti, l) for e in Ti.get_edges(): self.__congestion[e] += 1 lambda_ = max(lambda_, self.__congestion[e]) iteration = 1 while iteration <= 100: for i, r in enumerate(requests): cmax = max(self.__congestion.values()) E_Ti = MSTs[i][0].get_edges() A = len(E_Ti) self.__graph.update_edge_weights({ e: self.__costs[e] * A**(self.__congestion[e] - cmax) for e in self.__edges }) self.__graph.update_edge_weights( {e: le / A for e, le in E_Ti.iteritems()}) mz = VST_RS(self.__graph) Ti_, l, _, _, _, _, _ = mz.steiner_forest( r[1:], [r[0]], k, sys.maxint) self.__graph.update_edge_weights( {e: le * A for e, le in E_Ti.iteritems()}) if MSTs[i][1] > l: for e in MSTs[i][0].get_edges(): self.__congestion[e] -= 1 for e in Ti_.get_edges(): self.__congestion[e] += 1 MSTs[i] = (Ti_, l) iteration += 1 return MSTs
def __build_steiner_tree_bactracking(self, node, subset): steiner_tree = SuitabilityGraph() next_node = self.__s_d[node][tuple(subset)][1] if self.__contract_graph: print(node, self.__s_d[node][tuple(subset)]) # pdb.set_trace() if next_node is not None: steiner_tree.append_path(self.__paths[tuple(sorted([node, next_node]))], self.__graph) (best_e, best_f) = self.__s_d[node][tuple(subset)][2] # pdb.set_trace() steiner_branch_e = SuitabilityGraph() if best_e is not None and best_e != [next_node]: steiner_branch_e = self.__build_steiner_tree_bactracking(next_node, best_e) steiner_branch_f = SuitabilityGraph() if best_f is not None and best_f != [next_node] and len(best_f) > 0: steiner_branch_f = self.__build_steiner_tree_bactracking(next_node, best_f) steiner_tree.append_graph(steiner_branch_e) steiner_tree.append_graph(steiner_branch_f) return steiner_tree
def __build_steiner_tree(self, node, subset): steiner_tree = SuitabilityGraph() next_node = self.__s_d[node][subset][0][3] print(node, self.__s_d[node][subset]) # pdb.set_trace() if next_node is not None: try: steiner_tree.append_path( self.__paths[tuple(sorted([node, next_node]))], self.__graph) except KeyError: _, paths = dijkstra(self.__graph, node, [next_node]) steiner_tree.append_path(paths[next_node], self.__graph) (set_e, set_f) = self.__s_d[node][subset][0][4] steiner_branch_e = SuitabilityGraph() if set_e is not None and set_e != [next_node]: steiner_branch_e = self.__build_steiner_tree(next_node, set_e) steiner_branch_f = SuitabilityGraph() if set_f is not None and set_f != [next_node] and len(set_f) > 0: steiner_branch_f = self.__build_steiner_tree(next_node, set_f) steiner_tree.append_graph(steiner_branch_e) steiner_tree.append_graph(steiner_branch_f) return steiner_tree
def __build_steiner_tree_bactracking(self, node, subset): steiner_tree = SuitabilityGraph() next_node = self.__steiner_distances[node][tuple(subset)][3] # pdb.set_trace() if next_node is not None: try: steiner_tree.append_path( self.__paths[tuple(sorted([node, next_node]))], self.__graph) except KeyError: _, paths = dijkstra(self.__graph, node, [next_node]) steiner_tree.append_path(paths[next_node], self.__graph) (best_e, best_f) = self.__steiner_distances[node][tuple(subset)][4] steiner_branch_e = SuitabilityGraph() if best_e is not None and best_e != [next_node]: steiner_branch_e = self.__build_steiner_tree_bactracking( next_node, best_e) steiner_branch_f = SuitabilityGraph() if best_f is not None and best_f != [next_node] and len(best_f) > 0: steiner_branch_f = self.__build_steiner_tree_bactracking( next_node, best_f) steiner_tree.append_graph(steiner_branch_e) steiner_tree.append_graph(steiner_branch_f) return steiner_tree
class Gravitation: def __init__(self, graph, terminals, poi, max_level_attraction=2, contract_graph=True, contracted_graph=None, within_convex_hull=False, dist_paths_suitable_nodes=None): if not graph.is_node_weighted(): raise ( ValueError, "Gravitation algorithm only works with node-weighted graphs.") # Store class variables for future references. self.__original_graph = graph self.__terminals = terminals self.__poi = poi self.__contract_graph = contract_graph # terminals_poi = list(terminals) terminals_poi.append(poi) generator = SuitableNodeWeightGenerator() # Contracted graph... if contract_graph: if contracted_graph is not None: self.__graph = contracted_graph.copy() else: self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) self.__graph.contract_suitable_regions( generator, excluded_nodes=terminals_poi) else: self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) # # # ngh = NetworkXGraphHelper(self.__original_graph) # ngh.draw_graph(nodes_1=terminals, # nodes_2=[poi], # subgraphs_1=[r for _, (r, _, _) in self.__graph.contracted_regions.iteritems()], # node_weight_generator=generator, # node_size=25) # # # ngh = NetworkXGraphHelper(self.__graph) # ngh.draw_graph(node_weight_generator=generator, node_size=25, node_labels=True) # Copy distances and paths dictionary since it will be changed. dist_paths = None if dist_paths_suitable_nodes is not None: dist_paths = dict(dist_paths_suitable_nodes) for e in terminals_poi: dist_paths[e] = dijkstra(self.__graph, e) # Get the suitable nodes. if within_convex_hull: self.__suitable_nodes = self.__graph.get_suitable_nodes_within_convex_set( terminals_poi, generator, dist_paths) else: self.__suitable_nodes = self.__graph.get_suitable_nodes( generator, excluded_nodes=terminals_poi) # print(self.__suitable_nodes) # self.__dist_paths_node_node = {} if dist_paths is not None: self.__dist_paths_node_node = { n: dist_paths[n] for n in self.__suitable_nodes } else: self.__dist_paths_node_node = \ {n: dijkstra(self.__graph, n) for n in self.__suitable_nodes} for e in terminals_poi: if e not in self.__dist_paths_node_node: self.__dist_paths_node_node[e] = dijkstra(self.__graph, e) # max_distances = [ max(self.__dist_paths_node_node[n][0].values()) for n in self.__suitable_nodes if len(self.__dist_paths_node_node[n][0].values()) > 0 ] if len(max_distances) > 0: self.__max_dist = max(max_distances) else: self.__max_dist = 0 # # # max_level_attraction_poi = 0 # for t in terminals: # max_level_attraction_poi = max(max_level_attraction_poi, len(self.__dist_paths_node_node[poi][1][t])) # mass = self.__calculate_mass_suitable_node(poi) # self.__attract_nodes_to(poi, mass, poi, max_level_attraction_poi, 0, []) # dist_to_poi = {} for n in self.__suitable_nodes: try: dist_to_poi[n] = self.__dist_paths_node_node[n][0][poi] except KeyError: dist_to_poi[n] = sys.maxint # dist_to_poi = {n: self.__dist_paths_node_node[n][0][poi] for n in self.__suitable_nodes} # ord_suit_nodes = sorted(dist_to_poi.iteritems(), key=operator.itemgetter(1), reverse=True) ord_suit_nodes = sorted(dist_to_poi.iteritems(), key=operator.itemgetter(1)) for n, _ in ord_suit_nodes: mass = self.__calculate_mass_suitable_node(n) self.__attract_nodes_to(n, mass, n, max_level_attraction, 0, []) # # # ngh = NetworkXGraphHelper(self.__graph) # ngh.draw_graph(node_weight_generator=generator, node_size=25, node_labels=False) ''' ''' def __calculate_mass_suitable_node(self, node): hits = 0 for t in self.__terminals: p = self.__dist_paths_node_node[self.__poi][1][t] if node in p: hits += 10 mass = 0 if node in self.__graph.contracted_regions: region = self.__graph.contracted_regions[node][0] for n in region: mass += 1. / region[n][0] * 100 else: mass = 1. / self.__graph[node][0] * 100 return mass + hits ''' ''' def __calculate_adjusted_edge_cost(self, edge_cost, mass_attracting_node, distance_from_attracting_node): if self.__max_dist > 0: scaled_dist = float( distance_from_attracting_node) / self.__max_dist else: scaled_dist = 1 return edge_cost * scaled_dist / mass_attracting_node ''' ''' def __attract_nodes_to(self, attracting_node, mass_attracting_node, current_node, level_attraction, distance_so_far, already_affected_edges): if level_attraction < 1: return for neighbour in self.__graph[current_node][1]: edge = tuple(sorted([current_node, neighbour])) distance_from_attracting_node = self.__dist_paths_node_node[ attracting_node][0][neighbour] edge_cost = self.__graph[current_node][1][neighbour] if distance_so_far + edge_cost == distance_from_attracting_node and edge not in already_affected_edges: adjusted_edge_cost = \ self.__calculate_adjusted_edge_cost(edge_cost, mass_attracting_node, distance_from_attracting_node) self.__graph[current_node][1][neighbour] = adjusted_edge_cost self.__graph[neighbour][1][current_node] = adjusted_edge_cost already_affected_edges.append(edge) self.__attract_nodes_to(attracting_node, mass_attracting_node, neighbour, level_attraction - 1, distance_so_far + edge_cost, already_affected_edges) def __find_closest_node_to_node_within_region(self, node, region_id): region = self.__graph.contracted_regions[region_id][0] min_dist = sys.maxint closest_node = None distances, paths = dijkstra(self.__original_graph, node, region.keys()) for n in region: if distances[n] < min_dist: closest_node = n min_dist = distances[n] return closest_node, paths[closest_node] def __decontract_steiner_tree(self, steiner_tree): regions = [] paths = [] trees = [] for r in steiner_tree: if r in self.__graph.contracted_regions: regions.append(r) neighbors = steiner_tree[r][1].keys() new_terminals = [] for n in neighbors: closest_node_to_n, path = self.__find_closest_node_to_node_within_region( n, r) paths.append(path) new_terminals.append(closest_node_to_n) del steiner_tree[n][1][r] if len(new_terminals) > 1: region = self.__graph.contracted_regions[r][0] g = Gravitation(region, new_terminals[1:], new_terminals[0], contract_graph=False) st = g.steiner_tree() trees.append(st) for r in regions: del steiner_tree[r] for p in paths: steiner_tree.append_path(p, self.__original_graph) for st in trees: steiner_tree.append_graph(st) # Fix the edge costs. for v in steiner_tree: for w in steiner_tree[v][1]: steiner_tree[v][1][w] = self.__original_graph[v][1][w] @staticmethod def __merge_subtrees(subtrees): result = SuitabilityGraph() for subtree in subtrees: result.append_graph(subtree) return result def __prune_steiner_tree(self, steiner_tree): while True: keys_to_prune = [] for n in steiner_tree: if n not in self.__terminals and n != self.__poi: neighbours = steiner_tree[n][1].keys() if len(neighbours) == 1: neighbour = neighbours[0] del steiner_tree[neighbour][1][n] keys_to_prune.append(n) if len(keys_to_prune) == 0: break for n in keys_to_prune: del steiner_tree[n] def steiner_tree(self): subtrees = [] _, paths = dijkstra(self.__graph, self.__poi, self.__terminals) for t in self.__terminals: path = paths[t] subtree = SuitabilityGraph() # j = 0 # for i in range(len(path_to_poi)): # if path_to_poi[i] in self.__graph.contracted_regions: # region_id = path_to_poi[i] # subtree.append_from_path(path_to_poi[j:i], self.__original_graph) # closest_node = self.__find_closest_node_to_poi_within_region(region_id) # path_endpoint_1 = self.__dist_paths_node_within_region_node[closest_node][1][path_to_poi[i - 1]] # subtree.append_from_path(path_endpoint_1, self.__original_graph) # path_endpoint_2 = self.__dist_paths_node_within_region_node[closest_node][1][path_to_poi[i + 1]] # subtree.append_from_path(path_endpoint_2, self.__original_graph) # j = i + 1 # subtree.append_from_path(path_to_poi[j:], self.__original_graph) subtree.append_path(path, self.__graph) subtrees.append(subtree) steiner_tree = self.__merge_subtrees(subtrees) self.__prune_steiner_tree(steiner_tree) if self.__contract_graph: self.__decontract_steiner_tree(steiner_tree) return steiner_tree
def __merge_subtrees(subtrees): result = SuitabilityGraph() for subtree in subtrees: result.append_graph(subtree) return result
class LazySteinerTree: def __init__(self, graph, terminals, hot_spots=None, generator=None, distances=None): # Check whether graph is node-weighted. if not graph.is_node_weighted(): raise (ValueError, "Lazy Steiner Tree only works with node-weighted graphs.") # Extract POI from the terminals list. if len(terminals) > 0: self.__poi = terminals[0] else: return # Set object variables. self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) self.__terminals = terminals self.__hot_spots = None self.__nodes = None self.__s_d = {} self.__paths = {} self.__refs = {} # Set hot spots. if hot_spots is None: if generator is None: generator = SuitableNodeWeightGenerator() self.__hot_spots = self.__graph.get_suitable_nodes( generator, excluded_nodes=terminals) else: self.__hot_spots = list(hot_spots) # Set nodes = hot spots + terminals. self.__nodes = list(self.__hot_spots) for t in terminals: self.__nodes.append(t) # Set distances. if distances is None: len_hot_spots = len(self.__hot_spots) self.__distances = {} for t in self.__terminals: dist, paths = dijkstra(self.__graph, t, self.__nodes) for n in self.__nodes: try: self.__distances[tuple(sorted([t, n]))] = (dist[n], 'N') self.__paths[tuple(sorted([t, n]))] = paths[n] except KeyError: self.__distances[tuple(sorted([t, n]))] = (sys.maxint, 'N') self.__paths[tuple(sorted([t, n]))] = [] for h1 in self.__hot_spots: for i in range(self.__hot_spots.index(h1), len_hot_spots): h2 = self.__hot_spots[i] distance = 0 d_type = 'E' if h1 == h2: d_type = 'N' else: distance = haversine(self.__graph[h1][2]['lat'], self.__graph[h1][2]['lon'], self.__graph[h2][2]['lat'], self.__graph[h2][2]['lon']) self.__distances[tuple(sorted([h1, h2]))] = (distance, d_type) else: self.__distances = dict(distances) ''' ''' @staticmethod def __create_subsets_e(set_): sets_e = [tuple([set_[0]])] l_set = len(set_) for x in range(1, l_set - 1): for y in comb(set_[1:], x): t = [set_[0]] t.extend(y) sets_e.append(tuple(t)) return sets_e def steiner_tree(self, h_l_sd=20, h_l_hot_spots=3, consider_terminals=False): # set_c = tuple(sorted(self.__terminals[1:])) t_tuples = [tuple([t]) for t in set_c] # for j in self.__nodes: self.__s_d[j] = {} for t in t_tuples: dist, d_t = self.__distances[tuple(sorted([j, t[0]]))] self.__s_d[j][t] = [[ dist, 0, dist, t[0], (None, None), d_t, d_t, d_t, d_t, 0 ]] # for m in range(2, len(set_c)): # sets_d = [tuple(set_d) for set_d in comb(set_c, m)] for set_d in sets_d: # target_hot_spots = CandidatesList(h_l_hot_spots) for i in self.__nodes: self.__s_d[i][set_d] = CandidatesList(h_l_sd) # sets_e = self.__create_subsets_e(set_d) # for j in self.__nodes: u = sys.maxint sets_e_f = None d_ts = None for set_e in sets_e: set_f = tuple(sorted(list(set(set_d) - set(set_e)))) if len(set_f) > 0: s = self.__s_d[j][set_e][0][0] + self.__s_d[j][ set_f][0][0] else: s = self.__s_d[j][set_e][0][0] if s < u: u = s sets_e_f = (set_e, set_f) d_ts = (self.__s_d[j][set_e][0][5], self.__s_d[j][set_f][0][5]) for i in self.__nodes: try: dist, d_t = self.__distances[tuple(sorted([i, j]))] except KeyError: dist = sys.maxint d_t = 'N' if consider_terminals: cost = dist + u # if cost < self.__steiner_distances[i][set_d][0][0]: if cost < sys.maxint: dd_t = 'E' if d_t == 'N' and d_ts[0] == 'N' and d_ts[ 1] == 'N': dd_t = 'N' self.__s_d[i][set_d].append([ cost, u, dist, j, sets_e_f, dd_t, d_t, d_ts[0], d_ts[1], 0 ]) # dist_to_poi = self.__distances[tuple(sorted([self.__poi, i]))][0] # target_hot_spots.append([dist_to_poi + cost, i]) else: cost = dist + u # if cost < self.__steiner_distances[i][set_d][0][0] and j not in self.__terminals: if j not in self.__terminals and cost < sys.maxint: dd_t = 'E' if d_t == 'N' and d_ts[0] == 'N' and d_ts[ 1] == 'N': dd_t = 'N' self.__s_d[i][set_d].append([ cost, u, dist, j, sets_e_f, dd_t, d_t, d_ts[0], d_ts[1], 0 ]) # dist_to_poi = self.__distances[tuple(sorted([self.__poi, i]))][0] # target_hot_spots.append([dist_to_poi + cost, i]) target_hot_spots = CandidatesList(h_l_hot_spots) for i in self.__nodes: cost = self.__s_d[i][set_d][0][0] j = self.__s_d[i][set_d][0][3] set_e, set_f = self.__s_d[i][set_d][0][4] dist_to_poi = self.__distances[tuple( sorted([self.__poi, i]))][0] target_hot_spots.append([dist_to_poi + cost, i]) if j in self.__refs: if set_e in self.__refs[j]: self.__refs[j][set_e].add((i, set_d)) else: self.__refs[j][set_e] = {(i, set_d)} if set_f in self.__refs[j]: self.__refs[j][set_f].add((i, set_d)) else: self.__refs[j][set_f] = {(i, set_d)} else: self.__refs[j] = { set_e: {(i, set_d)}, set_f: {(i, set_d)} } # which is the best node for steiner tree between terminals in D and POI # print('-------------------------------------------------------') # print(set_d) # print('-------------------------------------------------------') # self.__print_target_hot_spots(target_hot_spots, set_d) # print('-------------------------------------------------------') # pdb.set_trace() self.__correct_i_s(target_hot_spots, set_d) self.__print_target_hot_spots(target_hot_spots, set_d) # print('-------------------------------------------------------') # sets_e = self.__create_subsets_e(set_c) # # cost = sys.maxint target_hot_spots = CandidatesList(h_l_hot_spots) self.__s_d[self.__poi][set_c] = CandidatesList(h_l_sd) for j in self.__nodes: u = sys.maxint sets_e_f = None d_ts = None for set_e in sets_e: set_f = tuple(sorted(list(set(set_c) - set(set_e)))) if len(set_f) > 0: s = self.__s_d[j][set_e][0][0] + self.__s_d[j][set_f][0][0] else: s = self.__s_d[j][set_e][0][0] if s < u: u = s sets_e_f = (set_e, set_f) d_ts = (self.__s_d[j][set_e][0][5], self.__s_d[j][set_f][0][5]) try: dist, d_t = self.__distances[tuple(sorted([self.__poi, j]))] except KeyError: dist = sys.maxint d_t = 'N' if consider_terminals: # if dist + u < cost: dd_t = 'E' if d_t == 'N' and d_ts[0] == 'N' and d_ts[1] == 'N': dd_t = 'N' cost = dist + u if cost < sys.maxint: self.__s_d[self.__poi][set_c].append([ cost, u, dist, j, sets_e_f, dd_t, d_t, d_ts[0], d_ts[1], 0 ]) target_hot_spots.append([cost, self.__poi]) else: cost = dist + u if j not in self.__terminals and cost < sys.maxint: dd_t = 'E' if d_t == 'N' and d_ts[0] == 'N' and d_ts[1] == 'N': dd_t = 'N' self.__s_d[self.__poi][set_c].append([ cost, u, dist, j, sets_e_f, dd_t, d_t, d_ts[0], d_ts[1], 0 ]) target_hot_spots.append([cost, self.__poi]) j = self.__s_d[self.__poi][set_c][0][3] set_e, set_f = self.__s_d[self.__poi][set_c][0][4] if j in self.__refs: if set_e in self.__refs[j]: self.__refs[j][set_e].add((self.__poi, set_c)) else: self.__refs[j][set_e] = {(self.__poi, set_c)} if set_f in self.__refs[j]: self.__refs[j][set_f].add((self.__poi, set_c)) else: self.__refs[j][set_f] = {(self.__poi, set_c)} else: self.__refs[j] = { set_e: {(self.__poi, set_c)}, set_f: {(self.__poi, set_c)} } # print('-------------------------------------------------------') # print(set_c) # print('-------------------------------------------------------') # self.__print_target_hotspots(target_hot_spots, set_c) # print('-------------------------------------------------------') # pdb.set_trace() # self.__print_target_hot_spots(target_hot_spots, set_c) # pdb.set_trace() self.__correct_i_s(target_hot_spots, set_c) # print('-------------------------------------------------------') # # # while True: # delta_cost = self.__steinerify(self.__poi, set_c) # if delta_cost == 0: # break # # Reconstruct the Steiner by backtracking steiner_tree = self.__build_steiner_tree(self.__poi, set_c) # return steiner_tree # def __correct_i_s(self, target_hot_spots, subset): # while len(target_hot_spots) > 0: # # pdb.set_trace() # i = target_hot_spots[0][1] # dd_t = self.__s_d[i][subset][0][5] # if dd_t == 'E': # self.__correct_j_s(i, subset) # target_hot_spots.pop(0) def __correct_i_s(self, target_hot_spots, subset): if len(target_hot_spots) > 0: # target_hot_spots.sort() i = target_hot_spots[0][1] dd_t = self.__s_d[i][subset][0][5] # while dd_t == 'E': # delta = self.__correct_j_s(i, subset) target_hot_spots[0][0] += delta # target_hot_spots.sort() i = target_hot_spots[0][1] dd_t = self.__s_d[i][subset][0][5] def __propagate(self, delta, j, subset): try: for i, subset_i in self.__refs[j][subset]: # if i == 552618963: # print(subset_i, self.__s_d[i][subset_i][0][0], delta, j, subset) self.__s_d[i][subset_i][0][0] += delta self.__s_d[i][subset_i][0][1] += delta self.__propagate(delta, i, subset_i) except KeyError: pass def __correct_j_s(self, i, subset): # # self.__s_d[i][subset].sort() j = self.__s_d[i][subset][0][3] old_j = j old_set_e, old_set_f = self.__s_d[i][subset][0][4] dd_t = self.__s_d[i][subset][0][5] count = 0 # while dd_t == 'E': # ---------------------------------------------------------------------------------------------------------- delta_dist = 0 d_t_1 = self.__s_d[i][subset][0][6] if d_t_1 == 'E': # i_j_tuple = tuple(sorted([i, j])) if self.__distances[i_j_tuple][1] == 'N': dist = self.__distances[i_j_tuple][0] else: try: distances, _ = dijkstra(self.__graph, i, [j]) dist = distances[j] self.__distances[i_j_tuple] = (dist, 'N') except KeyError: dist = sys.maxint old_dist = self.__s_d[i][subset][0][2] delta_dist = dist - old_dist # self.__s_d[i][subset][0][0] += delta_dist self.__s_d[i][subset][0][2] = dist self.__s_d[i][subset][0][6] = 'N' # ---------------------------------------------------------------------------------------------------------- delta_u = 0 d_t_2 = self.__s_d[i][subset][0][7] d_t_3 = self.__s_d[i][subset][0][8] if d_t_2 == 'E' or d_t_3 == 'E': # if i == 3073194802L and subset == (30287961, 313278858, 1011956802, 1655220587): # pdb.set_trace() u, set_e, set_f, delta_e, delta_f = self.__correct_e_f( j, subset) self.__s_d[i][subset][0][4] = (set_e, set_f) delta_u = delta_e + delta_f # if u != self.__s_d[i][subset][0][1]: delta_u = u - self.__s_d[i][subset][0][1] self.__s_d[i][subset][0][0] += delta_u self.__s_d[i][subset][0][1] += delta_u self.__s_d[i][subset][0][7] = 'N' self.__s_d[i][subset][0][8] = 'N' # ---------------------------------------------------------------------------------------------------------- d_t_1 = self.__s_d[i][subset][0][6] d_t_2 = self.__s_d[i][subset][0][7] d_t_3 = self.__s_d[i][subset][0][8] if d_t_1 == 'N' and d_t_2 == 'N' and d_t_3 == 'N': self.__s_d[i][subset][0][5] = 'N' # delta = delta_dist + delta_u self.__s_d[i][subset][0][9] = delta if delta > 0 and count == 0: self.__propagate(delta, i, subset) if self.__s_d[i][subset][0][5] == 'N': try: del self.__refs[i][subset] except KeyError: pass count += 1 # self.__s_d[i][subset].sort() j = self.__s_d[i][subset][0][3] dd_t = self.__s_d[i][subset][0][5] # Update refs j = self.__s_d[i][subset][0][3] set_e, set_f = self.__s_d[i][subset][0][4] if j != old_j or set_e != old_set_e or set_f != old_set_f: # try: self.__refs[old_j][old_set_e].remove((i, subset)) self.__refs[old_j][old_set_f].remove((i, subset)) except KeyError: pass # if j in self.__refs: if set_e in self.__refs[j]: self.__refs[j][set_e].add((i, subset)) else: self.__refs[j][set_e] = {(i, subset)} if set_f in self.__refs[j]: self.__refs[j][set_f].add((i, subset)) else: self.__refs[j][set_f] = {(i, subset)} else: self.__refs[j] = {set_e: {(i, subset)}, set_f: {(i, subset)}} return self.__s_d[i][subset][0][9] def __create_subsets_e_f(self, j, set_): subsets_e_f = [] sets_e = self.__create_subsets_e(set_) for set_e in sets_e: set_f = tuple(sorted(list(set(set_) - set(set_e)))) if len(set_f) > 0: s = self.__s_d[j][set_e][0][0] + self.__s_d[j][set_f][0][0] else: s = self.__s_d[j][set_e][0][0] subsets_e_f.append([s, set_e, set_f, 0, 0]) return subsets_e_f def __correct_e_f(self, j, set_): # subsets_e_f = self.__create_subsets_e_f(j, set_) # subsets_e_f.sort() set_e = subsets_e_f[0][1] set_f = subsets_e_f[0][2] dd_t_e = self.__s_d[j][set_e][0][5] dd_t_f = self.__s_d[j][set_f][0][5] while dd_t_e == 'E' or dd_t_f == 'E': # delta_e = self.__correct_j_s(j, set_e) delta_f = self.__correct_j_s(j, set_f) subsets_e_f[0][0] += delta_e + delta_f subsets_e_f[0][3] = delta_e subsets_e_f[0][4] = delta_f # subsets_e_f.sort() set_e = subsets_e_f[0][1] set_f = subsets_e_f[0][2] dd_t_e = self.__s_d[j][set_e][0][5] dd_t_f = self.__s_d[j][set_f][0][5] return subsets_e_f[0] def __print_target_hot_spots(self, target_hot_spots, subset): for th in target_hot_spots: dist_to_poi = th[0] i = th[1] j = self.__s_d[i][subset][0][3] dd_type = self.__s_d[i][subset][0][5] d_type_1 = self.__s_d[i][subset][0][6] d_type_2 = self.__s_d[i][subset][0][7] d_type_3 = self.__s_d[i][subset][0][8] print(dist_to_poi, i, j, dd_type, d_type_1, d_type_2, d_type_3) def __build_steiner_tree(self, node, subset): steiner_tree = SuitabilityGraph() next_node = self.__s_d[node][subset][0][3] print(node, self.__s_d[node][subset]) # pdb.set_trace() if next_node is not None: try: steiner_tree.append_path( self.__paths[tuple(sorted([node, next_node]))], self.__graph) except KeyError: _, paths = dijkstra(self.__graph, node, [next_node]) steiner_tree.append_path(paths[next_node], self.__graph) (set_e, set_f) = self.__s_d[node][subset][0][4] steiner_branch_e = SuitabilityGraph() if set_e is not None and set_e != [next_node]: steiner_branch_e = self.__build_steiner_tree(next_node, set_e) steiner_branch_f = SuitabilityGraph() if set_f is not None and set_f != [next_node] and len(set_f) > 0: steiner_branch_f = self.__build_steiner_tree(next_node, set_f) steiner_tree.append_graph(steiner_branch_e) steiner_tree.append_graph(steiner_branch_f) return steiner_tree
generator = SuitableNodeWeightGenerator() # try: for seed in seeds: print("seed:", seed) for msns in range(len(ms)): print("nodes:", ms[msns] * ns[msns]) graph = GridDigraphGenerator().generate( ms[msns], ns[msns], node_weighted=True, node_weight_generator=generator, seed=seed) suitability_graph = SuitabilityGraph() suitability_graph.append_graph(graph) hotspots = suitability_graph.get_suitable_nodes(generator) start_time = time.clock() suitability_graph.compute_dist_paths(origins=hotspots, destinations=hotspots, compute_paths=False) print "compute", time.clock() - start_time, "# hotspots:", len( hotspots) for num_seats in capacity: # suitability_graph.extend_suitable_regions(seed, generator) # hotspots = suitability_graph.get_suitable_nodes(generator) # print i, "# hotspots:", len(hotspots) for num_terminals in nums_terminals: new_terminals = set()
class DreyfusIMRV2: def __init__(self, graph, terminals, contract_graph=True, contracted_graph=None, within_convex_hull=False, dist_paths=None, nodes=None, use_medoid=False): # Check whether graph is node-weighted. if not graph.is_node_weighted(): raise (ValueError, "Dreyfus with IMRs algorithm only works with node-weighted graphs.") # Extract POI from the terminals list. if len(terminals) > 0: self.__poi = terminals[0] else: return # Set object variables. generator = SuitableNodeWeightGenerator() self.__original_graph = graph self.__terminals = terminals self.__contract_graph = contract_graph self.__use_medoid = use_medoid # Contracted graph... if contract_graph: if contracted_graph is not None: self.__graph = contracted_graph.copy() else: self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) self.__graph.contract_suitable_regions(generator, excluded_nodes=terminals, get_centroid_medoid=use_medoid) else: self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) # if nodes is not None: self.__nodes = list(nodes) else: if within_convex_hull: pass # self.__nodes = self.__graph.get_suitable_nodes_within_convex_set(terminals, generator, dist_paths) else: self.__nodes = self.__graph.get_suitable_nodes(generator, excluded_nodes=terminals) # for t in terminals: self.__nodes.append(t) # print(self.__nodes) # self.__dist = {} self.__paths = {} if dist_paths is not None: self.__dist = dict(dist_paths[0]) self.__paths = dict(dist_paths[1]) else: self.__dist, self.__paths = self.__graph.get_dist_paths(origins=self.__nodes, destinations=self.__nodes) # self.__s_d = {} ''' ''' def steiner_tree(self, consider_terminals=False): # set_c = tuple(sorted(self.__terminals[1:])) t_tuples = [tuple([t]) for t in set_c] # for j in self.__nodes: self.__s_d[j] = {} for t in t_tuples: pair_nodes = tuple(sorted([j, t[0]])) try: entry_node = None if j in self.__graph.contracted_regions: path = self.__paths[pair_nodes] if len(path) > 1: if path.index(j) == len(path) - 1: entry_node = path[len(path) - 2] else: entry_node = path[1] else: # pdb.set_trace() pass self.__s_d[j][t] = [self.__dist[pair_nodes], t[0], (None, None), entry_node] except KeyError: self.__s_d[j][t] = [sys.maxint, t[0], (None, None), None] # for m in range(2, len(set_c)): # sets_d = [tuple(set_d) for set_d in comb(set_c, m)] for set_d in sets_d: # for i in self.__nodes: self.__s_d[i][set_d] = [sys.maxint, None, (None, None)] # sets_e = self.__create_subsets_e(set_d) # for j in self.__nodes: u = sys.maxint best_subsets = None for set_e in sets_e: set_f = tuple(sorted(list(set(set_d) - set(set_e)))) if len(set_f) > 0: s = self.__s_d[j][set_e][0] + self.__s_d[j][set_f][0] else: s = self.__s_d[j][set_e][0] # if s < u: u = s best_subsets = (set_e, set_f) for i in self.__nodes: pair_nodes = tuple(sorted([i, j])) # d_n1_n3 = d_n2_n3 = 0 d1 = d2 = d3 = 0 try: dist = self.__dist[pair_nodes] if j in self.__graph.contracted_regions and best_subsets is not None: e_n_1 = self.__s_d[j][best_subsets[0]][3] e_n_2 = self.__s_d[j][best_subsets[1]][3] path = self.__paths[pair_nodes] dropped_edges = self.__graph[j][2]['dropped_edges'] dist_paths = self.__graph[j][2]['dist_paths'] n1 = dropped_edges[e_n_1] n2 = dropped_edges[e_n_2] if len(path) > 1: if path.index(j) == len(path) - 1: e_n_3 = path[len(path) - 2] else: e_n_3 = path[1] n3 = dropped_edges[e_n_3] dr = DreyfusIMR(self.__graph.contracted_regions[j][0], terminals=[n1, n2, n3], contract_graph=False) st = dr.steiner_tree() d1, _ = st.compute_total_weights() # # Use the medoid to compute the internal distance. # if self.__use_medoid: # medoid = self.__graph.contracted_regions[j][4] # d1 = dist_paths[0][tuple(sorted([n1, medoid]))] # d2 = dist_paths[0][tuple(sorted([n2, medoid]))] # d3 = dist_paths[0][tuple(sorted([n3, medoid]))] # else: # d1 = dist_paths[0][tuple(sorted([n1, n3]))] # d2 = dist_paths[0][tuple(sorted([n2, n3]))] # elif j == i: # d1 = dist_paths[0][tuple(sorted([n1, n2]))] except KeyError: dist = sys.maxint cost = dist + u + d1 + d2 + d3 if consider_terminals: if cost < self.__s_d[i][set_d][0]: self.__s_d[i][set_d] = [cost, j, best_subsets] #####################IMPORTANT: NOT COMPLETELY IMPLEMENTED!!!##################### else: if cost < self.__s_d[i][set_d][0] and j not in self.__terminals: entry_node = None if i in self.__graph.contracted_regions: try: path = self.__paths[pair_nodes] if path.index(i) == len(path) - 1: entry_node = path[len(path) - 2] else: entry_node = path[1] except KeyError: pass self.__s_d[i][set_d] = [cost, j, best_subsets, entry_node] # sets_e = self.__create_subsets_e(set_c) # if self.__poi not in self.__s_d: self.__s_d[self.__poi] = {set_c: [sys.maxint, None, (None, None), None]} else: self.__s_d[self.__poi][set_c] = [sys.maxint, None, (None, None), None] # for j in self.__nodes: u = sys.maxint best_subsets = None for set_e in sets_e: set_f = tuple(sorted(list(set(set_c) - set(set_e)))) if len(set_f) > 0: s = self.__s_d[j][set_e][0] + self.__s_d[j][set_f][0] else: s = self.__s_d[j][set_e][0] if s < u: u = s best_subsets = (set_e, set_f) pair_nodes = tuple(sorted([self.__poi, j])) # d_n1_n3 = d_n2_n3 = 0 d1 = d2 = d3 = 0 try: dist = self.__dist[pair_nodes] if j in self.__graph.contracted_regions and best_subsets is not None: e_n_1 = self.__s_d[j][best_subsets[0]][3] e_n_2 = self.__s_d[j][best_subsets[1]][3] path = self.__paths[pair_nodes] if len(path) > 1: if path.index(j) == len(path) - 1: e_n_3 = path[len(path) - 2] else: e_n_3 = path[1] dropped_edges = self.__graph[j][2]['dropped_edges'] dist_paths = self.__graph[j][2]['dist_paths'] n1 = dropped_edges[e_n_1] n2 = dropped_edges[e_n_2] n3 = dropped_edges[e_n_3] dr = DreyfusIMR(self.__graph.contracted_regions[j][0], terminals=[n1, n2, n3], contract_graph=False) st = dr.steiner_tree() d1, _ = st.compute_total_weights() # Use the medoid to compute the internal distance. # if self.__use_medoid: # medoid = self.__graph.contracted_regions[j][4] # d1 = dist_paths[0][tuple(sorted([n1, medoid]))] # d2 = dist_paths[0][tuple(sorted([n2, medoid]))] # d3 = dist_paths[0][tuple(sorted([n3, medoid]))] # else: # d1 = dist_paths[0][tuple(sorted([n1, n3]))] # d2 = dist_paths[0][tuple(sorted([n2, n3]))] except KeyError: dist = sys.maxint cost = dist + u + d1 + d2 + d3 if consider_terminals: if cost < self.__s_d[self.__poi][set_c][0]: self.__s_d[self.__poi][set_c] = [cost, j, best_subsets] #####################IMPORTANT: NOT COMPLETELY IMPLEMENTED!!!##################### else: if cost < self.__s_d[self.__poi][set_c][0] and j not in self.__terminals: self.__s_d[self.__poi][set_c] = [cost, j, best_subsets, None] # Reconstruct the Steiner by backtracking steiner_tree = self.__build_steiner_tree_bactracking(self.__poi, set_c) if self.__contract_graph: self.__decontract_steiner_tree(steiner_tree) return steiner_tree ''' ''' @staticmethod def __create_subsets_e(set_): sets_e = [tuple([set_[0]])] l_set = len(set_) for x in range(1, l_set - 1): for y in comb(set_[1:], x): t = [set_[0]] t.extend(y) sets_e.append(tuple(t)) return sets_e ''' ''' def __build_steiner_tree_bactracking(self, node, subset): steiner_tree = SuitabilityGraph() next_node = self.__s_d[node][tuple(subset)][1] if self.__contract_graph: print(node, self.__s_d[node][tuple(subset)]) # pdb.set_trace() if next_node is not None: steiner_tree.append_path(self.__paths[tuple(sorted([node, next_node]))], self.__graph) (best_e, best_f) = self.__s_d[node][tuple(subset)][2] # pdb.set_trace() steiner_branch_e = SuitabilityGraph() if best_e is not None and best_e != [next_node]: steiner_branch_e = self.__build_steiner_tree_bactracking(next_node, best_e) steiner_branch_f = SuitabilityGraph() if best_f is not None and best_f != [next_node] and len(best_f) > 0: steiner_branch_f = self.__build_steiner_tree_bactracking(next_node, best_f) steiner_tree.append_graph(steiner_branch_e) steiner_tree.append_graph(steiner_branch_f) return steiner_tree ''' ''' def __find_closest_node_to_node_within_region(self, node, region_id): region = self.__graph.contracted_regions[region_id][0] min_dist = sys.maxint closest_node = None distances, paths = dijkstra(self.__original_graph, node, region.keys()) for n in region: if distances[n] < min_dist: closest_node = n min_dist = distances[n] return closest_node, paths[closest_node] ''' ''' def __decontract_steiner_tree(self, steiner_tree): to_drop = [] paths = [] trees = [] # pdb.set_trace() for n in steiner_tree: try: if n in self.__graph.contracted_regions: dropped_edges = steiner_tree[n][2]['dropped_edges'] neighbors = steiner_tree[n][1].keys() new_terminals = set() for b in neighbors: t = dropped_edges[b] new_terminals.add(t) if b in self.__graph.auxiliary_nodes: bb = [cc for cc in steiner_tree[b][1] if cc != n][0] paths.append([t, bb]) del steiner_tree[bb][1][b] to_drop.append(b) else: paths.append([t, b]) del steiner_tree[b][1][n] if len(new_terminals) > 1: region = self.__graph.contracted_regions[n][0] d = DreyfusIMRV2(region, list(new_terminals), contract_graph=False) st = d.steiner_tree() trees.append(st) to_drop.append(n) except KeyError: pass # pdb.set_trace() for r in to_drop: del steiner_tree[r] for p in paths: steiner_tree.append_path(p, self.__original_graph) for st in trees: steiner_tree.append_graph(st) # pdb.set_trace() # for r in steiner_tree: # if r in self.__graph.contracted_regions: # regions.append(r) # neighbors = steiner_tree[r][1].keys() # new_terminals = set() # for n in neighbors: # closest_node_to_n, path = self.__find_closest_node_to_node_within_region(n, r) # paths.append(path) # new_terminals.add(closest_node_to_n) # del steiner_tree[n][1][r] # if len(new_terminals) > 1: # region = self.__graph.contracted_regions[r][0] # d = DreyfusIMR(region, list(new_terminals), contract_graph=False) # st = d.steiner_tree() # trees.append(st) # for r in regions: # del steiner_tree[r] # for p in paths: # steiner_tree.append_from_path(p, self.__original_graph) # for st in trees: # steiner_tree.append_from_graph(st) def enclosing_region(self): enclosing = SuitabilityGraph() paths = [] paths.append(self.__paths[tuple(sorted([323287670, 2392803740]))]) paths.append(self.__paths[tuple(sorted([2392803740, 127578100]))]) paths.append(self.__paths[tuple(sorted([127578100, 3109398450]))]) paths.append(self.__paths[tuple(sorted([3109398450, 342909685]))]) paths.append(self.__paths[tuple(sorted([342909685, 323287670]))]) for path in paths: enclosing.append_path(path, self.__original_graph) return enclosing
class LazySteinerTreeV2: def __init__(self, graph, terminals, hot_spots=None, generator=None, distances=None): # Check whether graph is node-weighted. if not graph.is_node_weighted(): raise (ValueError, "Lazy Steiner Tree only works with node-weighted graphs.") # Extract POI from the terminals list. if len(terminals) > 0: self.__poi = terminals[0] else: return # Set object variables. self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) self.__terminals = terminals self.__hot_spots = None self.__nodes = None self.__steiner_distances = {} self.__paths = {} # Set hot spots. if hot_spots is None: if generator is None: generator = SuitableNodeWeightGenerator() self.__hot_spots = self.__graph.get_suitable_nodes( generator, excluded_nodes=terminals) else: self.__hot_spots = list(hot_spots) # Set nodes = hot spots + terminals. self.__nodes = list(self.__hot_spots) for t in terminals: self.__nodes.append(t) # Set distances. if distances is None: len_hot_spots = len(self.__hot_spots) self.__distances = {} for t in self.__terminals: dist, paths = dijkstra(self.__graph, t, self.__nodes) for n in self.__nodes: try: self.__distances[tuple(sorted([t, n]))] = (dist[n], 'N') self.__paths[tuple(sorted([t, n]))] = paths[n] except KeyError: self.__distances[tuple(sorted([t, n]))] = (sys.maxint, 'N') self.__paths[tuple(sorted([t, n]))] = [] for h1 in self.__hot_spots: for i in range(self.__hot_spots.index(h1), len_hot_spots): h2 = self.__hot_spots[i] distance = 0 d_type = 'E' if h1 == h2: d_type = 'N' else: distance = haversine(self.__graph[h1][2]['lat'], self.__graph[h1][2]['lon'], self.__graph[h2][2]['lat'], self.__graph[h2][2]['lon']) self.__distances[tuple(sorted([h1, h2]))] = (distance, d_type) else: self.__distances = dict(distances) def steiner_tree(self, qi=10, consider_terminals=False): # set_c = sorted(self.__terminals[1:]) # for j in self.__nodes: self.__steiner_distances[j] = {} for t in set_c: distance, d_type = self.__distances[tuple(sorted([j, t]))] self.__steiner_distances[j][tuple([t])] = [ distance, 0, distance, t, (None, None), d_type, d_type, d_type, d_type ] # for m in range(2, len(set_c)): # sets_d = [tuple(c) for c in comb(set_c, m)] for set_d in sets_d: for i in self.__nodes: self.__steiner_distances[i][set_d] = [ sys.maxint, 0, sys.maxint, None, (None, None), None, None, None, None ] # sets_e = [tuple([set_d[0]])] for x in range(1, m - 1): for y in comb(set_d[1:], x): t = [set_d[0]] t.extend(y) sets_e.append(tuple(t)) # j_subsets_ef = {} for j in self.__nodes: j_subsets_ef[j] = [] for set_e in sets_e: set_f = tuple(sorted(list(set(set_d) - set(set_e)))) u_e = self.__steiner_distances[j][set_e][0] u_f = 0 t_e = self.__steiner_distances[j][set_e][5] t_f = 'N' if len(set_f) > 0: u_f = self.__steiner_distances[j][set_f][0] t_f = self.__steiner_distances[j][set_f][5] j_subsets_ef[j].append( [set_e, set_f, u_e, u_f, t_e, t_f]) # temp = [] for i in self.__nodes: dist_to_poi = self.__distances[tuple( sorted([self.__poi, i]))][0] for j in self.__nodes: try: dist, t_dist = self.__distances[tuple( sorted([i, j]))] except KeyError: dist = sys.maxint t_dist = 'N' for j_subset_ef in j_subsets_ef[j]: set_e = j_subset_ef[0] set_f = j_subset_ef[1] u_e = j_subset_ef[2] u_f = j_subset_ef[3] t_e = j_subset_ef[4] t_f = j_subset_ef[5] temp.append([ dist_to_poi + dist + u_e + u_f, i, j, set_e, set_f ]) temp.sort() print(len(temp)) print(temp[0:11]) pdb.set_trace()
class Spiders: def __init__(self, graph, terminals, poi, contract_graph=True, contracted_graph=None, within_convex_hull=False, dist_paths_suitable_nodes=None): # Check whether graph is node-weighted. if not graph.is_node_weighted(): raise (ValueError, "Spiders algorithm only works with node-weighted graphs.") # Store class variables for future references. self.__original_graph = graph self.__terminals = terminals self.__poi = poi self.__contract_graph = contract_graph terminals_poi = list(terminals) terminals_poi.append(poi) generator = SuitableNodeWeightGenerator() # Contracted graph... if contract_graph: if contracted_graph is not None: self.__graph = contracted_graph.copy() else: self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) self.__graph.contract_suitable_regions( generator, excluded_nodes=terminals_poi) else: self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) # Copy distances and paths dictionary since it will be changed. dist_paths = None if dist_paths_suitable_nodes is not None: dist_paths = dict(dist_paths_suitable_nodes) for e in terminals_poi: dist_paths[e] = dijkstra(self.__graph, e) # Get the suitable nodes. if within_convex_hull: self.__suitable_nodes = self.__graph.get_suitable_nodes_within_convex_set( terminals_poi, generator, dist_paths) else: self.__suitable_nodes = self.__graph.get_suitable_nodes( generator, excluded_nodes=terminals_poi) # POI will be included in this list. self.__suitable_nodes.append(poi) # Calculate distances and paths between nodes. IMPORTANT: Only suitable nodes are regarded as start nodes. self.__dist_paths_node_node = {} # self.__dist_paths_node_within_region_node = {} if dist_paths is not None: self.__dist_paths_node_node = { n: dist_paths[n] for n in self.__suitable_nodes } # for n in self.__suitable_nodes: # self.__dist_paths_node_node[n] = dist_paths[n] # if n in self.__graph.contracted_regions: # region = self.__graph.contracted_regions[n][0] # for w in region: # self.__dist_paths_node_within_region_node[w] = \ # dijkstra(self.__original_graph, w, consider_node_weights=False) else: self.__dist_paths_node_node = \ {n: dijkstra(self.__graph, n) for n in self.__suitable_nodes} # for n in self.__suitable_nodes: # self.__dist_paths_node_node[n] = dijkstra(self.__graph, n, consider_node_weights=False) # if n in self.__graph.contracted_regions: # region = self.__graph.contracted_regions[n][0] # for w in region: # self.__dist_paths_node_within_region_node[w] = \ # dijkstra(self.__original_graph, w, consider_node_weights=False) for e in terminals_poi: if e not in self.__dist_paths_node_node: self.__dist_paths_node_node[e] = dijkstra(self.__graph, e) # For every terminal create a subtree which has such terminal as the only node. Each subtree is digraph. self.__subtrees = {} for s in terminals: subtree = SuitabilityGraph() subtree[s] = (self.__graph[s][0], {}) self.__subtrees[s] = subtree # IMPORTANT: This method calculates the distances and paths from suitable nodes only. self.__calculate_distances_paths_to_subtrees() ''' Calculate the distances and paths from every suitable node to every subtree. It also calculates the sorted list of subtrees by distance. It is called every time merging operations are performed. ''' def __calculate_distances_paths_to_subtrees(self): # Distances and paths node-to-subtree self.__dist_paths_node_subtree = {} self.__ordered_subtrees = {} # For every suitable node, get the tuple (distances, paths) to every subtree and store it as the value for # the entry -> node: (distances, paths) in a dictionary which is a class variable. # distances: dictionary with entries -> subtree: distance_to_nearest_node_in_the_subtree # paths: dictionary with entries -> subtree: path_to_nearest_node_in_the_subtree # path: list of nodes ordered from node to nearest node in the subtree for n in self.__suitable_nodes: for s in self.__subtrees: dist, nearest = self.__calculate_distance_node_subtree(n, s) if n not in self.__dist_paths_node_subtree: self.__dist_paths_node_subtree[n] = ({}, {}) self.__dist_paths_node_subtree[n][0][s] = dist try: self.__dist_paths_node_subtree[n][1][ s] = self.__dist_paths_node_node[n][1][nearest] except KeyError: self.__dist_paths_node_subtree[n][1][s] = [] # Once the distances dictionary is ready for the current node, a list of tuples (subtree, distance) ordered # by distance is calculated and stored as a value for the entry -> node: sorted_list_of_subtrees_by_distance # The entry is stored in a dictionary which is a class variable. ordering = sorted(self.__dist_paths_node_subtree[n][0].iteritems(), key=operator.itemgetter(1)) self.__ordered_subtrees[n] = ordering ''' Calculate the distance from node to subtree. It also returns the nearest node within the subtree that is a suitable node, or the terminal if it is the only node within the subtree. ''' def __calculate_distance_node_subtree(self, node, subtree): min_dist = sys.maxint nearest_n = None # Get the distance from the node to every node in the subtree that is a suitable node (or is the only node that # is a terminal) and return the nearest. for n in self.__subtrees[subtree]: if (len(self.__subtrees[subtree].keys()) == 1 and n in self.__terminals) or n in self.__suitable_nodes: try: d = self.__dist_paths_node_node[node][0][n] except KeyError: d = sys.maxint if d < min_dist: min_dist = self.__dist_paths_node_node[node][0][n] nearest_n = n return min_dist, nearest_n ''' Evaluate a ratio similar to Klein and Ravi paper. However, when a node is within a subtree, i.e. its distance to this subtree is zero, the number of subtrees, which acts as divisor in the ratio, is also affected. A fraction of this subtree is taken into account instead of it as a whole. In this way, I prevent this node having advantage over the others. ''' def __evaluate_ratio(self, node, i): dist_subtrees = 0 cont = 0 for j in range(i + 1): if self.__ordered_subtrees[node][j][1] != 0: dist_subtrees += self.__ordered_subtrees[node][j][1] else: cont += 1 return float(self.__graph[node][0] + dist_subtrees) / (i + 1 - cont / 2.) ''' Clever way to get the minimum ratio for a node. ''' def __get_quotient_cost(self, node): i = 1 k = len(self.__subtrees) ratio = self.__evaluate_ratio(node, i) while i < k - 1: if self.__ordered_subtrees[node][i + 1][1] < ratio: i += 1 ratio = self.__evaluate_ratio(node, i) else: break return ratio, i ''' Get the best node amongst the suitable nodes, i.e. the node with the minimum ratio. ''' def __get_best_node(self, min_threshold=None): best_avg = sys.maxint best_node = None best_r = None best_checked = False # Useful to prevent unnecessary double-check whether a node already belongs to a subtree best_found = False # Indicates whether the best node, so far, has been found within a subtree # For every suitable node, get the minimum value of the heuristic function. The goal is to obtain the node # with the minimum minimum value. If the nodes that are being compared have the same minimum value, other # heuristics are taken into account. See V. J. Rayward-Smith paper. for n in self.__suitable_nodes: min_by_node, r = self.__get_quotient_cost(n) if min_threshold < min_by_node < best_avg: best_avg = min_by_node best_node = n best_r = r best_checked = False # A new best node is likely not having been checked yet best_found = False # A new best node is likely not having been found within a subtree elif min_by_node == best_avg: # In case two nodes have the same minimum value dist_0 = self.__dist_paths_node_node[best_node][0][self.__poi] dist_1 = self.__dist_paths_node_node[n][0][self.__poi] # a) The best is the one with minimum distance to the POI. if dist_1 < dist_0: best_node = n best_r = r best_checked = False # A new best node is likely not having been checked yet best_found = False # A new best node is likely not having been found within a subtree continue # b) The best is the one that is not within a subtree. if not best_checked or (best_checked and best_found): if not best_checked: # To prevent checking again # Check if the best node is within any subtree. for i in range(best_r + 1): subtree = self.__ordered_subtrees[best_node][i][0] if best_node in self.__subtrees[subtree]: best_found = True break best_checked = True # Check id the current node is within any subtree. cur_found = False for i in range(r + 1): subtree = self.__ordered_subtrees[n][i][0] if n in self.__subtrees[subtree]: cur_found = True break # This corresponds to the (i) heuristic proposed in the paper when two nodes have the same value. # Only if the best node is within a subtree whereas the current node not, the latter becomes the new # best node. if best_found and not cur_found: best_node = n best_r = r best_checked = True # This new best node has been already checked best_found = False # This new best node is not within a subtree return best_node, best_avg ''' Merge the first two nearest subtrees of the node and the node as well. ''' def __merge_subtrees_and_node(self, node): # Get the first two nearest subtrees to the node. subtree_0 = self.__ordered_subtrees[node][0][0] subtree_1 = self.__ordered_subtrees[node][1][0] # Generate a random id for the new subtree. new_subtree_id = "s_" + utils.id_generator() # Init the new subtree with the nodes of the first nearest subtree. new_subtree = self.__subtrees[subtree_0].copy() # Append the nodes of the second nearest subtree to the new subtree. new_subtree.append_graph(self.__subtrees[subtree_1]) # Get the paths from the node to the first two nearest subtrees. path_0 = self.__dist_paths_node_subtree[node][1][subtree_0] path_1 = self.__dist_paths_node_subtree[node][1][subtree_1] # Append these paths to the new subtree. for path in [path_0, path_1]: new_subtree.append_path(path, self.__graph) # Remove the first two nearest subtrees from the list of subtrees. del self.__subtrees[subtree_0] del self.__subtrees[subtree_1] # Add the new subtree to the list of subtrees. self.__subtrees[new_subtree_id] = new_subtree # Calculate distances and paths for the new list of subtrees. self.__calculate_distances_paths_to_subtrees() ''' Merge a node with a subtree and append the subtree to the list of subtrees. ''' def __merge_subtree_and_node(self, subtree, node): # Generate a random id for the new subtree. new_subtree_id = "s_" + utils.id_generator() # Init the new subtree with the nodes of the subtree. new_subtree = self.__subtrees[subtree].copy() # Get the path from the node to the subtree. path = self.__dist_paths_node_subtree[node][1][subtree] # Append this path to the new subtree. new_subtree.append_path(path, self.__graph) # Remove the subtree from the list of subtrees. del self.__subtrees[subtree] # Add the new subtree to the list of subtrees. self.__subtrees[new_subtree_id] = new_subtree # Calculate distances and paths for the new list of subtrees. self.__calculate_distances_paths_to_subtrees() def __merge_subtrees_through_poi(self, subtree_with_poi, subtree): # Generate a random id for the new subtree. new_subtree_id = "s_" + utils.id_generator() # Init the new subtree with the nodes of the subtree with the POI. new_subtree = self.__subtrees[subtree_with_poi].copy() # Append the nodes of the second subtree to the new subtree. new_subtree.append_graph(self.__subtrees[subtree]) # Find the path through the POI between these two subtrees. min_dist = sys.maxint nearest_n = None if len(self.__subtrees[subtree].keys()) == 1: nearest_n = self.__subtrees[subtree].keys()[0] else: for n in self.__subtrees[subtree]: if n in self.__suitable_nodes: if self.__dist_paths_node_node[n][0][ self.__poi] < min_dist: min_dist = self.__dist_paths_node_node[n][0][ self.__poi] nearest_n = n if nearest_n is None: raise ( RuntimeError, "__merge_subtrees_through_poi: couldn't find the nearest point within the subtree." ) path = self.__dist_paths_node_node[self.__poi][1][nearest_n] # Append this path to the new subtree. new_subtree.append_path(path, self.__graph) # Remove the two subtrees from the list of subtrees. del self.__subtrees[subtree_with_poi] del self.__subtrees[subtree] # Add the new subtree to the list of subtrees. self.__subtrees[new_subtree_id] = new_subtree # Calculate distances and paths for the new list of subtrees. self.__calculate_distances_paths_to_subtrees() return path def __find_closest_node_to_node_within_region(self, node, region_id): region = self.__graph.contracted_regions[region_id][0] min_dist = sys.maxint closest_node = None distances, paths = dijkstra(self.__original_graph, node, region.keys()) for n in region: if distances[n] < min_dist: closest_node = n min_dist = distances[n] return closest_node, paths[closest_node] def __decontract_steiner_tree(self, steiner_tree): regions = [] paths = [] trees = [] for r in steiner_tree: if r in self.__graph.contracted_regions: regions.append(r) neighbors = steiner_tree[r][1].keys() new_terminals = [] for n in neighbors: closest_node_to_n, path = self.__find_closest_node_to_node_within_region( n, r) paths.append(path) new_terminals.append(closest_node_to_n) del steiner_tree[n][1][r] if len(new_terminals) > 1: region = self.__graph.contracted_regions[r][0] s = Spiders(region, new_terminals[1:], new_terminals[0], contract_graph=False) st, _ = s.steiner_tree() trees.append(st) for r in regions: del steiner_tree[r] for p in paths: steiner_tree.append_path(p, self.__original_graph) for st in trees: steiner_tree.append_graph(st) # def __find_closest_node_to_poi_within_region(self, region_id): # region = self.__graph.contracted_regions[region_id][0] # min_dist = sys.maxint # closest_node = None # distances, _ = dijkstra(self.__original_graph, self.__poi, region.keys(), consider_node_weights=False) # for n in region: # if distances[n] < min_dist: # closest_node = n # min_dist = distances[n] # return closest_node # # def __decontract_steiner_tree(self, steiner_tree): # regions = [] # paths = [] # for r in steiner_tree: # if r in self.__graph.contracted_regions: # regions.append(r) # closest_node = self.__find_closest_node_to_poi_within_region(r) # neighbors = steiner_tree[r][1].keys() # _, paths_closest = dijkstra(self.__original_graph, closest_node, neighbors, consider_node_weights=False) # for v in steiner_tree[r][1]: # paths.append(paths_closest[v]) # del steiner_tree[v][1][r] # for r in regions: # del steiner_tree[r] # for p in paths: # steiner_tree.append_from_path(p, self.__original_graph) def __prune_steiner_tree(self, steiner_tree): while True: keys_to_prune = [] for n in steiner_tree: if n not in self.__terminals and n != self.__poi: neighbours = steiner_tree[n][1].keys() if len(neighbours) == 1: neighbour = neighbours[0] del steiner_tree[neighbour][1][n] keys_to_prune.append(n) if len(keys_to_prune) == 0: break for n in keys_to_prune: del steiner_tree[n] ''' The minimum Steiner tree will be the last tree after merging all the subtrees. ''' def steiner_tree(self): # ngh = NetworkXGraphHelper(self.__graph) meeting_nodes = [] while len(self.__subtrees) > 1: # Get the best node according to the heuristic function. best_node, best_avg = self.__get_best_node() # Get the two subtrees that might be merged. subtree_0 = self.__ordered_subtrees[best_node][0][0] subtree_1 = self.__ordered_subtrees[best_node][1][0] # After the best node has been found, there are four possibilities: # a) If the two subtrees are the last ones and any of them contains the POI, I do not need an intermediary # node since there is not chance this meeting node will be used by other subtrees. Thus, I merge the # subtrees through the POI. if len(self.__subtrees) == 2: if self.__poi in self.__subtrees[ subtree_0] or self.__poi in self.__subtrees[subtree_1]: if self.__poi in self.__subtrees[subtree_0]: self.__merge_subtrees_through_poi(subtree_0, subtree_1) else: self.__merge_subtrees_through_poi(subtree_1, subtree_0) # meeting_nodes.append((self.__poi, self.__terminals)) continue # b) If one of the subtrees that might be merged contains the POI and there are more than two subtrees, # I avoid merging this subtree and search for another couple. while (self.__poi in self.__subtrees[subtree_0] or self.__poi in self.__subtrees[subtree_1]) \ and best_node != self.__poi: best_node, best_avg = self.__get_best_node(best_avg) if best_node is None: best_node, best_avg = self.__get_best_node() subtree_0 = self.__ordered_subtrees[best_node][0][0] subtree_1 = self.__ordered_subtrees[best_node][1][0] break subtree_0 = self.__ordered_subtrees[best_node][0][0] subtree_1 = self.__ordered_subtrees[best_node][1][0] # c) If one of them is closer to the POI than to the best node, I merge this subtree with the POI instead of # using the best node as an intermediary meeting node. dist_0 = sys.maxint dist_1 = sys.maxint # If the POI is within any of the subtrees, it does not make sense to check for convenience. if self.__poi not in self.__subtrees[subtree_0]: dist_0 = self.__dist_paths_node_subtree[ self.__poi][0][subtree_0] if self.__poi not in self.__subtrees[subtree_1]: dist_1 = self.__dist_paths_node_subtree[ self.__poi][0][subtree_1] # If one of them is closer to the POI... if dist_0 < self.__ordered_subtrees[best_node][0][ 1] or dist_1 < self.__ordered_subtrees[best_node][1][1]: # Merge the POI with the subtree. if dist_0 < self.__ordered_subtrees[best_node][0][1]: # meeting_nodes.append((self.__poi, [t for t in self.__subtrees[subtree_0] if t in self.__terminals])) self.__merge_subtree_and_node(subtree_0, self.__poi) if dist_1 < self.__ordered_subtrees[best_node][1][1]: # meeting_nodes.append((self.__poi, [t for t in self.__subtrees[subtree_1] if t in self.__terminals])) self.__merge_subtree_and_node(subtree_1, self.__poi) continue # d) The "best node" is in fact the best node. # nodes = [t for t in self.__subtrees[subtree_0] if t in self.__terminals] # nodes.extend([t for t in self.__subtrees[subtree_1] if t in self.__terminals]) # meeting_nodes.append((best_node, nodes)) self.__merge_subtrees_and_node(best_node) # legend = [str(counter + 1) + ". imp->" + str(mn[0]) + " for: " + str(mn[1]) for counter, mn in # enumerate(meeting_nodes)] # # ngh.draw_graph(nodes_2=[self.__poi], nodes_1=self.__terminals, # subgraphs_2=[subtree for _, subtree in self.__subtrees.iteritems()], # node_weight_generator=SuitableNodeWeightGenerator(), # legend=legend, # node_labels=True) # At the end, if the POI is not part of the Steiner tree, merge the POI with the Steiner tree because the POI is # a compulsory meeting node. if self.__poi not in self.__subtrees[self.__subtrees.keys()[0]]: # meeting_nodes.append( # (self.__poi, [t for t in self.__subtrees[self.__subtrees.keys()[0]] if t in self.__terminals])) self.__merge_subtree_and_node(self.__subtrees.keys()[0], self.__poi) steiner_tree = self.__subtrees[self.__subtrees.keys()[0]] self.__prune_steiner_tree(steiner_tree) if self.__contract_graph: self.__decontract_steiner_tree(steiner_tree) return steiner_tree, meeting_nodes
class LazySteinerTree: def __init__(self, graph, terminals, hot_spots=None, generator=None, distances=None): # Check whether graph is node-weighted. if not graph.is_node_weighted(): raise (ValueError, "Lazy Steiner Tree only works with node-weighted graphs.") # Extract POI from the terminals list. if len(terminals) > 0: self.__poi = terminals[0] else: return # Set object variables. self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) self.__terminals = terminals self.__hot_spots = None self.__nodes = None self.__steiner_distances = {} self.__paths = {} # Set hot spots. if hot_spots is None: if generator is None: generator = SuitableNodeWeightGenerator() self.__hot_spots = self.__graph.get_suitable_nodes( generator, excluded_nodes=terminals) else: self.__hot_spots = list(hot_spots) # Set nodes = hot spots + terminals. self.__nodes = list(self.__hot_spots) for t in terminals: self.__nodes.append(t) # Set distances. # len_hot_spots = len(self.__hot_spots) if distances is None: # np.random.seed(1) # len_nodes = len(self.__nodes) len_hot_spots = len(self.__hot_spots) self.__distances = {} for t in self.__terminals: dist, paths = dijkstra(self.__graph, t, self.__nodes) for n in self.__nodes: try: self.__distances[tuple(sorted([t, n]))] = (dist[n], 'N') self.__paths[tuple(sorted([t, n]))] = paths[n] except KeyError: self.__distances[tuple(sorted([t, n]))] = (sys.maxint, 'N') self.__paths[tuple(sorted([t, n]))] = [] for h1 in self.__hot_spots: for i in range(self.__hot_spots.index(h1), len_hot_spots): h2 = self.__hot_spots[i] distance = 0 d_type = 'E' if h1 == h2: d_type = 'N' else: distance = haversine(self.__graph[h1][2]['lat'], self.__graph[h1][2]['lon'], self.__graph[h2][2]['lat'], self.__graph[h2][2]['lon']) self.__distances[tuple(sorted([h1, h2]))] = (distance, d_type) # for n1 in self.__nodes: # for i in range(self.__nodes.index(n1), len_nodes): # n2 = self.__nodes[i] # # rnd = np.random.ranf() # # if rnd <= 0.1: # # dist, paths = dijkstra(self.__graph, n1, [n2], consider_node_weights=False) # # try: # # distance = dist[n2] # # except KeyError: # # distance = sys.maxint # # self.__distances[tuple(sorted([n1, n2]))] = (distance, 'N') # # else: # distance = calculate_distance(self.__graph[n1][2]['lat'], self.__graph[n1][2]['lon'], # self.__graph[n2][2]['lat'], self.__graph[n2][2]['lon']) # self.__distances[tuple(sorted([n1, n2]))] = (distance, 'E') else: self.__distances = dict(distances) ''' ''' def steiner_tree(self, history_length=10, consider_terminals=False): # set_c = sorted(self.__terminals[1:]) # for j in self.__nodes: self.__steiner_distances[j] = {} for t in set_c: # self.__steiner_distances[j][tuple([t])] = CandidatesList(history_length) distance, d_type = self.__distances[tuple(sorted([j, t]))] # self.__steiner_distances[j][tuple([t])].append([distance, t, (None, None), distance, 0, d_type]) self.__steiner_distances[j][tuple([t])] = [ distance, 0, distance, t, (None, None), d_type, d_type, d_type, d_type ] # for m in range(2, len(set_c)): # for set_d in comb(set_c, m): candidates = CandidatesList(history_length) for i in self.__nodes: self.__steiner_distances[i][tuple(set_d)] = [ sys.maxint, 0, sys.maxint, None, (None, None), None, None, None, None ] # self.__steiner_distances[i][tuple(set_d)] = CandidatesList(history_length) # sets_e = [[set_d[0]]] for x in range(1, m - 1): for y in comb(set_d[1:], x): t = [set_d[0]] t.extend(y) sets_e.append(t) # # Do it with nodes j \in best hotspots for subsets of D for j in self.__nodes: u = sys.maxint best_subsets = None d_types = None for set_e in sets_e: set_f = sorted(list(set(set_d) - set(set_e))) if len(set_f) > 0: s = self.__steiner_distances[j][tuple(set_e)][0] + \ self.__steiner_distances[j][tuple(set_f)][0] else: s = self.__steiner_distances[j][tuple(set_e)][0] if s < u: u = s best_subsets = (set_e, set_f) d_types = ( self.__steiner_distances[j][tuple(set_e)][5], self.__steiner_distances[j][tuple(set_f)][5]) for i in self.__nodes: try: dist, d_type = self.__distances[tuple( sorted([i, j]))] except KeyError: dist = sys.maxint d_type = 'N' if consider_terminals: cost = dist + u if cost < self.__steiner_distances[i][tuple( set_d)][0]: dd_type = 'E' if d_type == 'N' and d_types[ 0] == 'N' and d_types[1] == 'N': dd_type = 'N' self.__steiner_distances[i][tuple(set_d)] = [ cost, u, dist, j, best_subsets, dd_type, d_type, d_types[0], d_types[1] ] dist_to_poi = self.__distances[tuple( sorted([self.__poi, i]))][0] candidates.append([ dist_to_poi + cost, cost, dist, i, j, dd_type, d_type, d_types[0], d_types[1], best_subsets ]) else: cost = dist + u if cost < self.__steiner_distances[i][tuple( set_d)][0] and j not in self.__terminals: dd_type = 'E' if d_type == 'N' and d_types[ 0] == 'N' and d_types[1] == 'N': dd_type = 'N' self.__steiner_distances[i][tuple(set_d)] = [ cost, u, dist, j, best_subsets, dd_type, d_type, d_types[0], d_types[1] ] dist_to_poi = self.__distances[tuple( sorted([self.__poi, i]))][0] candidates.append([ dist_to_poi + cost, cost, dist, i, j, dd_type, d_type, d_types[0], d_types[1], best_subsets ]) # which is the best node for steiner tree between terminals in D and POI # pdb.set_trace() print( '-------------------------------------------------------') print(set_d) print( '-------------------------------------------------------') print(candidates) print( '-------------------------------------------------------') self.__stabilize_candidates_set(candidates, set_d) sets_e = [[set_c[0]]] for x in range(1, len(set_c) - 1): for y in comb(set_c[1:], x): t = [set_c[0]] t.extend(y) sets_e.append(t) # cost = sys.maxint candidates = CandidatesList(history_length) if self.__poi not in self.__steiner_distances: self.__steiner_distances[self.__poi] = { tuple(set_c): [cost, 0, cost, None, (None, None), None, None, None, None] } else: self.__steiner_distances[self.__poi][tuple(set_c)] = [ cost, 0, cost, None, (None, None), None, None, None, None ] # for j in self.__nodes: u = sys.maxint best_subsets = None d_types = None for set_e in sets_e: set_f = sorted(list(set(set_c) - set(set_e))) if len(set_f) > 0: s = self.__steiner_distances[j][tuple(set_e)][0] + \ self.__steiner_distances[j][tuple(set_f)][0] else: s = self.__steiner_distances[j][tuple(set_e)][0] if s < u: u = s best_subsets = (set_e, set_f) d_types = (self.__steiner_distances[j][tuple(set_e)][5], self.__steiner_distances[j][tuple(set_f)][5]) try: dist, d_type = self.__distances[tuple(sorted([self.__poi, j]))] except KeyError: dist = sys.maxint d_type = 'N' if consider_terminals: if dist + u < cost: dd_type = 'E' if d_type == 'N' and d_types[0] == 'N' and d_types[ 1] == 'N': dd_type = 'N' cost = dist + u self.__steiner_distances[self.__poi][tuple(set_c)] = [ cost, u, dist, j, best_subsets, dd_type, d_type, d_types[0], d_types[1] ] candidates.append([ dist + cost, cost, dist, self.__poi, j, dd_type, d_type, d_types[0], d_types[1], best_subsets ]) else: if dist + u < cost and j not in self.__terminals: dd_type = 'E' if d_type == 'N' and d_types[0] == 'N' and d_types[ 1] == 'N': dd_type = 'N' cost = dist + u self.__steiner_distances[self.__poi][tuple(set_c)] = [ cost, u, dist, j, best_subsets, dd_type, d_type, d_types[0], d_types[1] ] candidates.append([ dist + cost, cost, dist, self.__poi, j, dd_type, d_type, d_types[0], d_types[1], best_subsets ]) # pdb.set_trace() print('-------------------------------------------------------') print(set_c) print('-------------------------------------------------------') print(candidates) print('-------------------------------------------------------') self.__stabilize_candidates_set(candidates, set_c) # # # while True: # delta_cost = self.__steinerify(self.__poi, set_c) # if delta_cost == 0: # break # # Reconstruct the Steiner by backtracking steiner_tree = self.__build_steiner_tree_bactracking(self.__poi, set_c) # return steiner_tree def __stabilize_candidates_set(self, candidates, subset): candidates.sort() candidate = candidates[0] while candidate[5] == 'E': d_type_1 = candidate[6] d_type_2 = candidate[7] d_type_3 = candidate[8] n1 = candidate[3] n2 = candidate[4] # pdb.set_trace() if d_type_1 == 'E' and d_type_2 == 'N' and d_type_3 == 'N': dist_poi_and_cost = candidate[0] cost = candidate[1] dist = candidate[2] if self.__distances[tuple(sorted([n1, n2]))][1] == 'N': dist_n2 = self.__distances[tuple(sorted([n1, n2]))][0] else: distances, _ = dijkstra(self.__graph, n1, [n2]) try: dist_n2 = distances[n2] except KeyError: print("Distance couldn't be found between:", n1, n2) break self.__steiner_distances[n1][tuple( subset)][0] = cost - dist + dist_n2 self.__steiner_distances[n1][tuple(subset)][2] = dist_n2 self.__steiner_distances[n1][tuple(subset)][5] = 'N' self.__steiner_distances[n1][tuple(subset)][6] = 'N' candidates[0][0] = dist_poi_and_cost - dist + dist_n2 candidates[0][2] = dist_n2 candidates[0][5] = 'N' candidates[0][6] = 'N' candidates.sort() candidate = candidates[0] elif not (d_type_1 == 'N' and d_type_2 == 'N' and d_type_3 == 'N'): # pdb.set_trace() best_e, best_f = candidate[9] print('----BEST E----') print(self.__steiner_distances[n2][tuple(best_e)]) # hold_e = False # hold_f = False j_e = self.__steiner_distances[n2][tuple(best_e)][3] d_typ_1_e = self.__steiner_distances[n2][tuple(best_e)][6] d_typ_2_e = self.__steiner_distances[n2][tuple(best_e)][7] d_typ_3_e = self.__steiner_distances[n2][tuple(best_e)][8] if d_typ_1_e == 'E' and d_typ_2_e == 'N' and d_typ_3_e == 'N': if self.__distances[tuple(sorted([n2, j_e]))][1] == 'N': dist_j_e = self.__distances[tuple(sorted([n2, j_e]))][0] else: distances, _ = dijkstra(self.__graph, n2, [j_e]) try: dist_j_e = distances[j_e] except KeyError: break u_e = self.__steiner_distances[n2][tuple(best_e)][1] self.__steiner_distances[n2][tuple( best_e)][0] = u_e + dist_j_e self.__steiner_distances[n2][tuple(best_e)][2] = dist_j_e self.__steiner_distances[n2][tuple(best_e)][5] = 'N' self.__steiner_distances[n2][tuple(best_e)][6] = 'N' hold_e = True elif d_typ_1_e == 'N' and d_typ_2_e == 'N' and d_typ_3_e == 'N': hold_e = True else: print('Bad news!', n2, j_e, best_e) break print('----BEST F----') print(self.__steiner_distances[n2][tuple(best_f)]) j_f = self.__steiner_distances[n2][tuple(best_f)][3] d_typ_1_f = self.__steiner_distances[n2][tuple(best_f)][6] d_typ_2_f = self.__steiner_distances[n2][tuple(best_f)][7] d_typ_3_f = self.__steiner_distances[n2][tuple(best_f)][8] if d_typ_1_f == 'E' and d_typ_2_f == 'N' and d_typ_3_f == 'N': if self.__distances[tuple(sorted([n2, j_f]))][1] == 'N': dist_j_f = self.__distances[tuple(sorted([n2, j_f]))][0] else: distances, _ = dijkstra(self.__graph, n2, [j_f]) try: dist_j_f = distances[j_f] except KeyError: break u_f = self.__steiner_distances[n2][tuple(best_f)][1] self.__steiner_distances[n2][tuple( best_f)][0] = u_f + dist_j_f self.__steiner_distances[n2][tuple(best_f)][2] = dist_j_f self.__steiner_distances[n2][tuple(best_f)][5] = 'N' self.__steiner_distances[n2][tuple(best_f)][6] = 'N' hold_f = True elif d_typ_1_f == 'N' and d_typ_2_f == 'N' and d_typ_3_f == 'N': hold_f = True else: print('Bad news!', n2, j_f, best_f) break # Update every node that is pointing to n2 if hold_e and hold_f: new_u = self.__steiner_distances[n2][tuple(best_e)][0] + \ self.__steiner_distances[n2][tuple(best_f)][0] for i in self.__nodes: if self.__steiner_distances[i][tuple(subset)][3] == n2 and \ self.__steiner_distances[i][tuple(subset)][4][0] == best_e and \ self.__steiner_distances[i][tuple(subset)][4][1] == best_f: # pdb.set_trace() dist = self.__steiner_distances[i][tuple( subset)][2] self.__steiner_distances[i][tuple( subset)][0] = dist + new_u self.__steiner_distances[i][tuple( subset)][1] = new_u self.__steiner_distances[i][tuple(subset)][7] = 'N' self.__steiner_distances[i][tuple(subset)][8] = 'N' # Update candidates for i in range(len(candidates)): c = candidates[i] if c[3] == n1 and c[4] == n2 and candidate[9][ 0] == best_e and candidate[9][1] == best_f: # pdb.set_trace() dist_poi_and_cost = candidates[i][0] cost = candidates[i][1] candidates[i][0] = dist_poi_and_cost - cost + new_u candidates[i][1] = new_u candidates[i][7] = 'N' candidates[i][8] = 'N' elif d_type_1 == 'N' and d_type_2 == 'N' and d_type_3 == 'N': candidates[0][5] = 'N' print(candidates) ''' ''' def __steinerify(self, parent_node, subset): # pdb.set_trace() # child_node = self.__steiner_distances[parent_node][tuple(subset)][0][1] initial_cost = self.__steiner_distances[parent_node][tuple( subset)][0][0] self.__stabilize_children(parent_node, subset) delta_cost = self.__steiner_distances[parent_node][tuple( subset)][0][0] - initial_cost new_child_node = self.__steiner_distances[parent_node][tuple( subset)][0][1] # if parent_node != self.__poi and (child_node != new_child_node or delta_cost > 0): # if delta_cost < 0: # delta_cost = 0 # return delta_cost if parent_node != self.__poi and delta_cost > 0: return delta_cost best_e, best_f = self.__steiner_distances[parent_node][tuple( subset)][0][2] delta_cost_e = delta_cost_f = 0 if best_e is not None and best_e != [new_child_node]: delta_cost_e = self.__steinerify(new_child_node, best_e) if best_f is not None and best_f != [new_child_node ] and len(best_f) > 0: delta_cost_f = self.__steinerify(new_child_node, best_f) self.__steiner_distances[parent_node][tuple( subset)][0][4] += delta_cost_e + delta_cost_f # Update u dist = self.__steiner_distances[parent_node][tuple(subset)][0][3] u = self.__steiner_distances[parent_node][tuple(subset)][0][4] self.__steiner_distances[parent_node][tuple( subset)][0][0] = dist + u # Update total cost if delta_cost_e + delta_cost_f > 0: return delta_cost_e + delta_cost_f return 0 ''' ''' def __stabilize_children(self, parent_node, subset): self.__steiner_distances[parent_node][tuple(subset)].sort() child = self.__steiner_distances[parent_node][tuple(subset)][0] while child[5] == 'E': child_node = child[1] distances, paths = dijkstra(self.__graph, parent_node, [child_node]) # for k, v in distances.iteritems(): # self.__distances[tuple(sorted([parent_node, k]))] = (v, 'N') # for k, v in paths.iteritems(): # self.__paths[tuple(sorted([parent_node, k]))] = v # try: # self.__paths[tuple(sorted([parent_node, child_node]))] = paths[child_node] # except KeyError: # pass # # print("KeyError!") try: # Update total cost of the parent node for this candidate child. self.__steiner_distances[parent_node][tuple( subset)][0][0] = child[4] + distances[child_node] # Update distance between the parent and this candidate child. self.__steiner_distances[parent_node][tuple( subset)][0][3] = distances[child_node] except KeyError: self.__steiner_distances[parent_node][tuple( subset)][0][0] = sys.maxint self.__steiner_distances[parent_node][tuple( subset)][0][3] = sys.maxint # Advise the distance between nodes is a network distance. self.__steiner_distances[parent_node][tuple(subset)][0][5] = 'N' # Sort the candidates list to check if priorities have changed. self.__steiner_distances[parent_node][tuple(subset)].sort() # Retrieve new best candidate. child = self.__steiner_distances[parent_node][tuple(subset)][0] ''' ''' def __build_steiner_tree_bactracking(self, node, subset): steiner_tree = SuitabilityGraph() next_node = self.__steiner_distances[node][tuple(subset)][3] # pdb.set_trace() if next_node is not None: try: steiner_tree.append_path( self.__paths[tuple(sorted([node, next_node]))], self.__graph) except KeyError: _, paths = dijkstra(self.__graph, node, [next_node]) steiner_tree.append_path(paths[next_node], self.__graph) (best_e, best_f) = self.__steiner_distances[node][tuple(subset)][4] steiner_branch_e = SuitabilityGraph() if best_e is not None and best_e != [next_node]: steiner_branch_e = self.__build_steiner_tree_bactracking( next_node, best_e) steiner_branch_f = SuitabilityGraph() if best_f is not None and best_f != [next_node] and len(best_f) > 0: steiner_branch_f = self.__build_steiner_tree_bactracking( next_node, best_f) steiner_tree.append_graph(steiner_branch_e) steiner_tree.append_graph(steiner_branch_f) return steiner_tree
class DreyfusIMR: def __init__(self, graph, terminals, contract_graph=True, contracted_graph=None, within_convex_hull=False, dist_paths=None, nodes=None): # Check whether graph is node-weighted. if not graph.is_node_weighted(): raise ( ValueError, "Dreyfus with IMRs algorithm only works with node-weighted graphs." ) # Extract POI from the terminals list. if len(terminals) > 0: self.__poi = terminals[0] else: return # Set object variables. generator = SuitableNodeWeightGenerator() self.__original_graph = graph self.__terminals = terminals self.__contract_graph = contract_graph # Contracted graph... if contract_graph: if contracted_graph is not None: self.__graph = contracted_graph.copy() else: self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) self.__graph.contract_suitable_regions( generator, excluded_nodes=terminals) else: self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) # if nodes is not None: self.__nodes = list(nodes) else: if within_convex_hull: pass # self.__nodes = self.__graph.get_suitable_nodes_within_convex_set(terminals, generator, dist_paths) else: self.__nodes = self.__graph.get_suitable_nodes( generator, excluded_nodes=terminals) # for t in terminals: self.__nodes.append(t) # print(self.__nodes) # self.__dist = {} self.__paths = {} if dist_paths is not None: self.__dist = dict(dist_paths[0]) self.__paths = dict(dist_paths[1]) else: self.__dist, self.__paths = self.__graph.get_dist_paths( origins=self.__nodes, destinations=self.__nodes) # self.__s_d = {} ''' ''' def steiner_tree(self, consider_terminals=False): # set_c = tuple(sorted(self.__terminals[1:])) t_tuples = [tuple([t]) for t in set_c] # for j in self.__nodes: self.__s_d[j] = {} for t in t_tuples: try: self.__s_d[j][t] = [ self.__dist[tuple(sorted([j, t[0]]))], t[0], (None, None) ] except KeyError: self.__s_d[j][t] = [sys.maxint, t[0], (None, None)] # for m in range(2, len(set_c)): # sets_d = [tuple(set_d) for set_d in comb(set_c, m)] for set_d in sets_d: # for i in self.__nodes: self.__s_d[i][set_d] = [sys.maxint, None, (None, None)] # sets_e = self.__create_subsets_e(set_d) # for j in self.__nodes: u = sys.maxint best_subsets = None for set_e in sets_e: set_f = tuple(sorted(list(set(set_d) - set(set_e)))) if len(set_f) > 0: s = self.__s_d[j][set_e][0] + self.__s_d[j][set_f][ 0] else: s = self.__s_d[j][set_e][0] if s < u: u = s best_subsets = (set_e, set_f) for i in self.__nodes: try: dist = self.__dist[tuple(sorted([i, j]))] except KeyError: dist = sys.maxint if consider_terminals: if dist + u < self.__s_d[i][set_d][0]: self.__s_d[i][set_d] = [ dist + u, j, best_subsets ] else: if dist + u < self.__s_d[i][set_d][ 0] and j not in self.__terminals[1:]: self.__s_d[i][set_d] = [ dist + u, j, best_subsets ] # sets_e = self.__create_subsets_e(set_c) # cost = sys.maxint if self.__poi not in self.__s_d: self.__s_d[self.__poi] = {set_c: [cost, None, (None, None)]} else: self.__s_d[self.__poi][set_c] = [cost, None, (None, None)] # for j in self.__nodes: u = sys.maxint best_subsets = None for set_e in sets_e: set_f = tuple(sorted(list(set(set_c) - set(set_e)))) if len(set_f) > 0: s = self.__s_d[j][set_e][0] + self.__s_d[j][set_f][0] else: s = self.__s_d[j][set_e][0] if s < u: u = s best_subsets = (set_e, set_f) try: dist = self.__dist[tuple(sorted([self.__poi, j]))] except KeyError: dist = sys.maxint if consider_terminals: if dist + u < cost: cost = dist + u self.__s_d[self.__poi][set_c] = [cost, j, best_subsets] else: if dist + u < cost and j not in self.__terminals[1:]: cost = dist + u self.__s_d[self.__poi][set_c] = [cost, j, best_subsets] # Reconstruct the Steiner by backtracking steiner_tree = self.__build_steiner_tree_bactracking(self.__poi, set_c) if self.__contract_graph: self.__decontract_steiner_tree(steiner_tree) return steiner_tree ''' ''' @staticmethod def __create_subsets_e(set_): sets_e = [tuple([set_[0]])] l_set = len(set_) for x in range(1, l_set - 1): for y in comb(set_[1:], x): t = [set_[0]] t.extend(y) sets_e.append(tuple(t)) return sets_e ''' ''' def __build_steiner_tree_bactracking(self, node, subset): steiner_tree = SuitabilityGraph() next_node = self.__s_d[node][tuple(subset)][1] print(node, self.__s_d[node][tuple(subset)]) # pdb.set_trace() if next_node is not None: steiner_tree.append_path( self.__paths[tuple(sorted([node, next_node]))], self.__graph) (best_e, best_f) = self.__s_d[node][tuple(subset)][2] # pdb.set_trace() steiner_branch_e = SuitabilityGraph() if best_e is not None and best_e != [next_node]: steiner_branch_e = self.__build_steiner_tree_bactracking( next_node, best_e) steiner_branch_f = SuitabilityGraph() if best_f is not None and best_f != [next_node] and len(best_f) > 0: steiner_branch_f = self.__build_steiner_tree_bactracking( next_node, best_f) steiner_tree.append_graph(steiner_branch_e) steiner_tree.append_graph(steiner_branch_f) return steiner_tree ''' ''' def __find_closest_node_to_node_within_region(self, node, region_id): region = self.__graph.contracted_regions[region_id][0] min_dist = sys.maxint closest_node = None distances, paths = dijkstra(self.__original_graph, node, region.keys()) for n in region: if distances[n] < min_dist: closest_node = n min_dist = distances[n] return closest_node, paths[closest_node] ''' ''' def __decontract_steiner_tree(self, steiner_tree): regions = [] paths = [] trees = [] for n in steiner_tree: try: if n in self.__graph.contracted_regions: dropped_edges = steiner_tree[n][2]['dropped_edges'] neighbors = steiner_tree[n][1].keys() new_terminals = set() for b in neighbors: t = dropped_edges[b] new_terminals.add(t) if b in self.__graph.auxiliary_nodes: bb = [cc for cc in steiner_tree[b][1] if cc != n][0] paths.append([t, bb]) else: paths.append([t, b]) if len(new_terminals) > 1: region = self.__graph.contracted_regions[n][0] d = DreyfusIMR(region, list(new_terminals), contract_graph=False) st = d.steiner_tree() trees.append(st) regions.append(n) except KeyError: pass for r in regions: del steiner_tree[r] for p in paths: steiner_tree.append_path(p, self.__original_graph) for st in trees: steiner_tree.append_graph(st)
m = n = 30 # m = n = 10 gh = GridDigraphGenerator() node_weighted = gh.generate(m, n, edge_weighted=True, node_weighted=True, node_weight_generator=generator, seed=seed) terminals = [288, 315, 231, 312, 111, 609, 645, 434, 654, 469, 186] # terminals = [36, 78, 28, 11] suitability_graph = SuitabilityGraph() suitability_graph.append_graph(node_weighted) suitability_graph.extend_suitable_regions(seed, generator) suitability_graph.extend_suitable_regions(seed, generator) regions = suitability_graph.get_suitable_regions(generator) start_time = time.clock() dr = DreyfusIMRV2(suitability_graph, terminals, contract_graph=True, within_convex_hull=False) steiner_tree = dr.steiner_tree(consider_terminals=False) elapsed_time = time.clock() - start_time cost, node_cost = steiner_tree.compute_total_weights(terminals)
results = [] try: for seed in range(num_seeds): for size in sizes: graph = GridDigraphGenerator().generate( size, size, node_weighted=True, node_weight_generator=generator, seed=seed) suitability_graph = SuitabilityGraph() suitability_graph.append_graph(graph) total_num_suitable_nodes = len( suitability_graph.get_suitable_nodes(generator)) for num_terminals in nums_terminals: for sample in range(num_samples): line = [ seed, size * size, total_num_suitable_nodes, num_terminals, sample + 1 ] terminals = np.random.choice(a=size * size, size=num_terminals,
class ClusterBased: def __init__(self, graph, terminals): # Check whether graph is node-weighted. if not graph.is_node_weighted(): raise ( ValueError, "Cluster-based algorithm only works with node-weighted graphs." ) # Extract POI from the terminals list. if len(terminals) > 0: self.__poi = terminals[0] else: return # generator = SuitableNodeWeightGenerator() # Set object variables. self.__graph = SuitabilityGraph() self.__graph.append_graph(graph) self.__terminals = terminals # # self.__regions = self.__graph.get_suitable_regions(generator, excluded_nodes=terminals, # get_border_internal_nodes=True, get_centroid_medoid=True, # get_characteristic_nodes=True) self.__regions = self.__graph.get_suitable_regions( generator, excluded_nodes=terminals, get_border_internal_nodes=True, get_centroid_medoid=True) # self.__nodes = [] for id_r in self.__regions: # # characteristic_nodes = self.__regions[id_r][6] # self.__nodes.extend(characteristic_nodes) border_nodes = self.__regions[id_r][1] self.__nodes.extend(border_nodes) # # if len(characteristic_nodes) == 0: # medoid = self.__regions[id_r][4] # # print(medoid) # self.__nodes.append(medoid) for t in terminals: self.__nodes.append(t) # self.__dist, self.__paths = self.__graph.get_dist_paths( origins=self.__nodes, destinations=self.__nodes) ''' ''' def steiner_tree(self, consider_terminals=False): dr = DreyfusIMR(graph=self.__graph, terminals=self.__terminals, contract_graph=False, nodes=self.__nodes, dist_paths=(self.__dist, self.__paths)) if consider_terminals: steiner_tree = dr.steiner_tree(consider_terminals=True) else: steiner_tree = dr.steiner_tree(consider_terminals=False) # self.__refine_steiner_tree(steiner_tree) return steiner_tree ''' ''' def __refine_steiner_tree(self, steiner_tree): clusters = [] for id_r in self.__regions: region = self.__regions[id_r][0] for n in steiner_tree: if n in region: clusters.append(id_r) break print clusters