예제 #1
0
def dijkstra(g, source, weights, destinations=None):
    """Return all the shortest paths from the source."""

    bests = heapdict()
    dists = {}
    paths = {}
    bests[source] = 0
    paths[source] = []

    visited = set([])
    visiteddests = set([])

    while len(bests) != 0:
        u, d = bests.popitem()
        dists[u] = d
        visited.add(u)
        if destinations and u in destinations:
            visiteddests.add(u)
            if len(visiteddests) == len(destinations):
                return dists, paths

        for e in u.incident_edges:
            v = e.neighbor(u)
            if v in visited:
                continue
            if v not in bests or d + weights[e] < bests[v]:
                bests[v] = d + weights[e]
                paths[v] = paths[u] + [e]

    return dists, paths
예제 #2
0
파일: flac.py 프로젝트: mouton5000/PACE2018
def compute(instance):

    #
    # "Global" parameters: those parameters are not reinitialized at each iteration
    #

    # The solution to be returned
    tree = Tree(instance.g, instance.weights)

    # Associate the couples of nodes (u,v) and (v,u) to the edge {u,v}
    couple_to_edges = {}
    for e in instance.g.edges:
        u, v = e.extremities
        couple_to_edges[(u, v)] = e
        couple_to_edges[(v, u)] = e

    def get_weight(u, v):
        """ Return the weight of the couple associated with the couple (u, v). """
        return instance.weights[couple_to_edges[(u, v)]]

    # Copy of the required vertices of the instance. It let the algorithm
    # modify this set without modifiying the instance.
    required_vertices = set(instance.terms)

    # Set arbitrary root
    root = random.choice(instance.terms)
    required_vertices.remove(root)

    # Set of nodes reached by root in the current tree
    reached = {root}

    # For each node, this dict sorts its input arcs by cost.
    sorted_input_arcs = defaultdict()

    def get_sorted_input_arcs(v):
        try:
            return sorted_input_arcs[v]
        except KeyError:

            def edge_to_arc(e):
                u, w = e.extremities
                if v == u:
                    return w, v
                else:
                    return u, v

            arcs = sorted_input_arcs[v] = list(
                map(
                    edge_to_arc,
                    sorted(v.incident_edges,
                           key=lambda e: instance.weights[e])))
            return arcs

    #
    # "Local" parameters: those parameters are reinitialized at each iteration
    #

    # Saturated edges (full of water)
    saturated = set()

    # For each node, this dict saves the sources this node is linked to with a path of saturated arcs
    sources = defaultdict(set)

    # For each node, this dict saves SOME OF the sources the ancestors of this node are linked to with a path of
    # saturated arcs. sources_of_ancestors may not contain all those sources. sources_of_ancestors[i] always include
    # sources[i]
    sources_of_ancestors = defaultdict(set)

    # Set of nodes for which one entering arc is saturating. The key of each node in the Fibonacci Heap is a triplet:
    # - the physical time when its next saturating arc will be saturated.
    # - a boolean : False if the input node is already reached by the tree, True otherwise
    # - an integer : the index of the output node of the edge
    # The key are compared using the natural python3 comparison of the triplets (float, boolean, integer)
    sorted_saturating = heapdict()

    #  For each node, this dict saves iterators pointing to the next saturated entering arc of this node.
    next_saturated_entering_arc_iterators = {}

    # For each node, this dict saves the next saturated entering arc of this node.
    next_saturated_entering_arcs = {}

    # The actual "physical" time since the beginning of saturation
    time = 0

    def update_next_saturated_arc(v):

        # print('Update', v, end=' ')

        # Iterator over the entering arcs of v sorted by weights
        try:
            it = next_saturated_entering_arc_iterators[v]
        except KeyError:
            it = next_saturated_entering_arc_iterators[v] = iter(
                get_sorted_input_arcs(v))
            # print(list(get_sorted_input_arcs(v)))

        # Last saturated arc entering v, may be None if the saturation in v just started
        b = next_saturated_entering_arcs.get(v, None)

        try:
            # If b was not the arc entering v with biggest cost, a exists
            # print('b:', b, 'v:', v, end=' ')
            next_saturated_entering_arcs[v] = a = next(it)
            # print('a:', a)
        except StopIteration:
            # Else, all arcs entering v are already saturated
            # print('No a available')
            del next_saturated_entering_arcs[v]
            del next_saturated_entering_arc_iterators[v]
            return

        # Compute saturated time of a
        if b is None:
            satTime = get_weight(*a) / len(sources[v])
        else:
            satTime = (get_weight(*a) - get_weight(*b)) / len(sources[v])
        sorted_saturating[v] = (time + satTime, a[0] not in reached, v.index)

    def reinit():
        nonlocal time
        """ Clear the maps, sets and lists used by the algorithm FLAC. Reinitialize the parameters used by FLAC. """
        saturated.clear()
        sources.clear()
        sources_of_ancestors.clear()
        sorted_saturating.clear()
        next_saturated_entering_arc_iterators.clear()
        next_saturated_entering_arcs.clear()
        time = 0

        for v in required_vertices:
            # Define the sources feeding that terminal as the terminal itself
            sources[v].add(v)
            sources_of_ancestors[v].add(v)

            # define the next saturated arc entering v, and compute the time in seconds needed to saturate it.
            update_next_saturated_arc(v)

    def next_saturated_arc():
        """ Return the next saturated time entering the first node if the fibonnacci heap. """
        nonlocal time
        v, k = sorted_saturating.popitem()
        time = k[0]
        return next_saturated_entering_arcs[v]

    def update_ancestor_sources(w, successors):
        """
        Update, for each node v which is a descendant of w in the list of successors, the set of sources
        an ancestor of v can reach with a path of saturated arcs :
        put all the sources an ancestor of w can reach into the vector of the successor v of w
        and then recursively do it with v and all the descendant until the successor of v is null.
        """
        srcs = sources_of_ancestors[w]

        toUpdate = successors.get(w, None)
        while toUpdate is not None:
            sources_of_ancestors[toUpdate] |= srcs
            srcs = sources_of_ancestors[toUpdate]
            toUpdate = successors.get(toUpdate, None)

    def find_conflict(u, v):
        """ Return true if the saturation of arc (u,v) implies a conflict. """
        # Will contain the list of nodes linked to u with a saturated path including u
        to_check = [u]

        # If one of those nodes is already linked to one of the sources linked to v there is a conflict.
        vsrcs = sources[v]

        # Contain, for each node w, the successor of w in in path of saturated arc from w to u, if such a path exists.
        successors = {}

        # print('>', u, v)

        while len(to_check) != 0:
            w = to_check.pop(0)
            # print('>>', w)
            # If the sources reaching w intersect the sources reaching v there is a conflict
            if not sources_of_ancestors[w].isdisjoint(vsrcs):
                update_ancestor_sources(w, successors)
                return True

            saturated_input_arc = next_saturated_entering_arcs.get(w, None)
            for u, _ in get_sorted_input_arcs(w):
                if saturated_input_arc is not None and u == saturated_input_arc[
                        0]:
                    break
                if (u, w) in saturated:
                    to_check.append(u)
                    successors[w] = u

    def saturate_arc_and_update(u, v):
        # This list will contain the set of node for which the flow rate will change after the saturation of a
        to_update = [u]

        vsrcs = sources[v]

        # print('Start sat', u, v)

        while len(to_update) != 0:
            w = to_update.pop(0)
            # print('Check', w)

            # The current flow rate inside each entering arc of w, before a is saturated
            previous_volume_flow_rate = len(sources[w])
            sources[w] |= vsrcs  # disjoint union, because there is no conflict
            sources_of_ancestors[w] |= vsrcs

            if previous_volume_flow_rate != 0:
                # if w already received flow before a became saturated
                # the time the next entering arc of w is saturated is accelerated like this:
                try:
                    previous_sat_time, is_reached, index = sorted_saturating[w]
                    new_volume_flow_rate = previous_volume_flow_rate + len(
                        vsrcs)
                    new_sat_time = time + (
                        previous_sat_time - time
                    ) * previous_volume_flow_rate / new_volume_flow_rate
                    sorted_saturating[w] = (new_sat_time, is_reached, index)
                except KeyError:
                    # All arcs entering w are fully saturated
                    pass
            else:
                # if w did not receive any flow from the source, we initialize its saturation like this
                update_next_saturated_arc(w)

            # For each node linked to w with a saturated arc, we insert it in the list
            # of nodes we have to update

            saturating_input_arc = next_saturated_entering_arcs.get(w, None)
            for input_arc in get_sorted_input_arcs(w):
                if input_arc == saturating_input_arc:
                    break
                if input_arc in saturated:
                    to_update.append(input_arc[0])

        # print('Sat', (u, v))
        saturated.add((u, v))

    def build_tree(u):
        to_check = [u]
        tree = set()
        reached = set()
        leaves = set()

        while len(to_check) != 0:
            v = to_check.pop(0)
            reached.add(v)
            if v in required_vertices:
                leaves.add(v)

            for u, _ in get_sorted_input_arcs(v):
                # We search for the "ouptut arcs" of v, as each "input arc" is an edge, if (u, v) is in the graph,
                # (v, u) is too.
                if not (v, u) in saturated:
                    continue
                tree.add(couple_to_edges[(v, u)])
                to_check.append(u)
        return tree, reached, leaves

    def apply_flac():
        """a tree rooted in the root of the instance spanning a part of the terminals, and the set of those
        terminals."""
        reinit()
        while True:
            # Check which arc will be the next saturated one
            u, v = next_saturated_arc()
            # print(a, end=' ')

            # If the root is reached by the terminals, we can return a tree
            if u in reached:
                saturated.add((u, v))
                return build_tree(u)

            # We now check if a node is linked to the root with two paths of saturated arcs: it is called a conflict
            conflict = find_conflict(u, v)
            # print(conflict)

            # Whatever the case, we have to check which arc of v will be its next saturated entering arc, and when
            # it will be saturated
            update_next_saturated_arc(v)

            # If there is a conflict, we just ignore the arc saturation, as if it was never added to the arc
            if not conflict:
                # If there is no conflict, we have to update the flow rate of other arcs as a new arc is saturated.
                saturate_arc_and_update(u, v)

    while len(required_vertices) != 0:

        best_tree, reached_nodes, reached_terminals = apply_flac()

        # if best_tree is None:  # Should never happen except if the instance is not feasible
        #     return None

        # For each node reached by tree, add that node to the set reached. As a consequence, the next tree returned by
        # FLAC is preferentially merged by the current partial solution. In addition, this loop add the arc to the
        # current partial solution.

        for e in best_tree:
            tree.add_edge(e)
        reached |= reached_nodes
        required_vertices -= reached_terminals

    tree.simplify(instance.terms)
    return tree
예제 #3
0
def voronoi(g, sources, weights):
    """Compute voronoi regions by parallely compute a dijkstra from each source. """

    n = len(g)

    # Closest source of each node
    closest_sources = {}

    # For each source, list the nodes that are at the limit of the voronoi region centered on that source.
    # For each such node v, list the edges (v, w) such that w is in another region.
    limits = defaultdict(lambda: defaultdict(set))

    # Heap of closest nodes of each source
    bests = {}
    current_bests = heapdict()
    for x in sources:
        h = heapdict()
        h[x] = 0
        bests[x] = h
        current_bests[x] = (0, x.index)

    # Distance from each source to its voronoi region nodes
    dists = defaultdict(dict)

    # Shortest path from each source to its voronoi region nodes
    paths = {}
    for x in sources:
        paths[x] = {x: []}

    while len(closest_sources) != n:
        x, _ = current_bests.popitem()
        h = bests[x]
        u, d = h.popitem()

        if u in closest_sources:
            if len(h) != 0:
                _, d2 = h.peekitem()
                current_bests[x] = (d2, x.index)
            continue

        dists[x][u] = d
        closest_sources[u] = x

        for e in u.incident_edges:
            v = e.neighbor(u)
            try:
                y = closest_sources[v]
                if x != y:
                    limits[x][u].add(e)
                    limits[y][v].add(e)
                continue
            except KeyError:
                pass
            if v not in h or d + weights[e] < h[v]:
                h[v] = d + weights[e]
                paths[x][v] = paths[x][u] + [e]

        if len(h) != 0:
            _, d2 = h.peekitem()
            current_bests[x] = (d2, x.index)
    return dists, paths, closest_sources, limits
예제 #4
0
def decremental_voronoi(g, sources, weights, dists, paths, closest_sources,
                        limits, rem_sources):
    """Update the voronoi regions of a graph where a set of sources is removed."""

    neighbor_sources = set([])

    # Heap of closest nodes of each source
    bests = defaultdict(lambda: heapdict())
    current_bests = heapdict()

    for y in rem_sources:
        del dists[y]
        del paths[y]
        for v, edges in limits[y].items():
            for e in edges:
                u = e.neighbor(v)
                try:
                    x = closest_sources[u]
                    if x in rem_sources:
                        continue

                    neighbor_sources.add(x)

                    h = bests[x]
                    d = dists[x][u]
                    h[u] = d

                    # Reinit u to start the iteration from u
                    # remove u from the closest sources list
                    # remove u from the limit region of x
                    # and remove every incident edge of u from the limit regions of the neighbors of x
                    # (except e, this is done after the for loop by removing y from the keys of the dict limits)
                    for ep in limits[x][u]:
                        if ep == e:
                            continue
                        w = ep.neighbor(u)
                        try:
                            z = closest_sources[w]
                            limits[z][w].remove(ep)
                        except KeyError:
                            pass
                    del closest_sources[u]
                    del limits[x][u]

                except KeyError:
                    pass

        del limits[y]

    for x in neighbor_sources:
        _, d2 = bests[x].peekitem()
        current_bests[x] = (d2, x.index)

    while len(current_bests) > 0:
        x, _ = current_bests.popitem()
        h = bests[x]
        u, d = h.popitem()

        try:
            if closest_sources[u] in neighbor_sources:
                if len(h) != 0:
                    _, d2 = h.peekitem()
                    current_bests[x] = (d2, x.index)
                continue
        except KeyError:
            pass

        dists[x][u] = d
        closest_sources[u] = x

        for e in u.incident_edges:
            v = e.neighbor(u)
            try:
                y = closest_sources[v]
                if y not in rem_sources:
                    if x != y:
                        limits[x][u].add(e)
                        limits[y][v].add(e)
                    continue
            except KeyError:
                pass
            if v not in h or d + weights[e] < h[v]:
                h[v] = d + weights[e]
                paths[x][v] = paths[x][u] + [e]

        if len(h) != 0:
            _, d2 = h.peekitem()
            current_bests[x] = (d2, x.index)

    return neighbor_sources
예제 #5
0
def incremental_voronoi(g, sources, weights, dists, paths, closest_sources,
                        limits, new_sources):
    """Update the voronoi regions of a graph where a set of new sources is added."""

    allvisited = set()

    # Heap of closest nodes of each source
    bests = {}
    current_bests = heapdict()
    for x in new_sources:
        h = heapdict()
        h[x] = 0
        bests[x] = h
        current_bests[x] = (0, x.index)

    for x in new_sources:
        paths[x] = {x: []}

    limits_edge_candidates = []

    while len(current_bests) > 0:
        x, _ = current_bests.popitem()
        h = bests[x]
        u, d = h.popitem()

        # u was already visited by a new source
        if u in allvisited:
            if len(h) != 0:
                _, d2 = h.peekitem()
                current_bests[x] = (d2, x.index)
            continue

        allvisited.add(u)

        y = closest_sources[u]

        if y != x:
            del dists[y][u]
            del paths[y][u]
            try:
                del limits[y][u]
            except KeyError:
                pass
        dists[x][u] = d
        closest_sources[u] = x

        for e in u.incident_edges:
            v = e.neighbor(u)
            if v in allvisited:
                y = closest_sources[v]
                if x != y:
                    limits[x][u].add(e)
                    limits[y][v].add(e)
                continue
            if v not in h:
                dv = d + weights[e]
                y = closest_sources[v]
                if dists[y][v] < dv or dists[y][v] == dv and y.index < x.index:
                    # Except if there is an other path from x to v that is shorter that dists[y][v]
                    # then u and v are at the limits of the regions of x and y
                    # in that case, when the while loop ends, x and y remains respectively the closest sources of u
                    # and v
                    limits_edge_candidates.append((x, y, u, v, e))
                else:
                    # dv is striclty better than the distance from y to v or dv equals that distance and x.index is
                    # lower than y.index
                    h[v] = dv
                    paths[x][v] = paths[x][u] + [e]
                continue
            if d + weights[e] < h[v]:
                h[v] = d + weights[e]
                paths[x][v] = paths[x][u] + [e]

        if len(h) != 0:
            _, d2 = h.peekitem()
            current_bests[x] = (d2, x.index)

    for x, y, u, v, e in limits_edge_candidates:
        if x == closest_sources[u] and y == closest_sources[v]:
            limits[x][u].add(e)
            limits[y][v].add(e)