예제 #1
0
 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
예제 #2
0
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
예제 #3
0
 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
예제 #4
0
 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
예제 #5
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
예제 #6
0
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
예제 #7
0
 def __merge_subtrees(subtrees):
     result = SuitabilityGraph()
     for subtree in subtrees:
         result.append_graph(subtree)
     return result
예제 #8
0
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
예제 #9
0
    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()
예제 #10
0
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
예제 #11
0
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()
예제 #12
0
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
예제 #13
0
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
예제 #14
0
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)
예제 #15
0
    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)
예제 #16
0
    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,
예제 #17
0
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