示例#1
0
def _shortest_x_tree(H,
                     source_node,
                     b_tree,
                     F=sum_function,
                     valid_ordering=False):
    """General form of the Shorest B-Tree algorithm, extended to also
    perform the implicit Shortest F-Tree procedure if the b_tree flag is
    not set (providing better time/memory performance than explcitily taking
    the hypergraph's symmetric image and then performing the SBT procedure
    on that).
    Uses priority queue to achieve O(size(H)*lg(n)) runtime.

    Refer to 'shorest_b_tree's or 'shorest_f_tree's documentation for
    more details.

    :param H: the H to perform the 'SXT' algorithm on.
    :param source_node: the root of the tree to be found.
    :param b_tree: boolean flag representing whether the Shortest B-Tree
                algorithm should be executed (vs the Shortest F-Tree).
    :param F: function pointer to any additive weight function; that is,
            any function that is only a function of the weights of the
            nodes in the tail of a hyperedge.
    :param valid_ordering: a boolean flag to signal whether or not a valid
                        ordering of the nodes should be returned.
    :returns:   dict -- mapping from each node to the ID of the hyperedge that
                     preceeded it in this traversal.
                dict -- mapping from each node to the node's weight.
                list -- [only if valid_ordering argument is passed] a valid
                        ordering of the nodes.
    :raises: TypeError -- Algorithm only applicable to directed hypergraphs

    """
    if not isinstance(H, DirectedHypergraph):
        raise TypeError("Algorithm only applicable to directed hypergraphs")

    if b_tree:
        forward_star = H.get_forward_star
        hyperedge_tail = H.get_hyperedge_tail
        hyperedge_head = H.get_hyperedge_head
    else:
        forward_star = H.get_backward_star
        hyperedge_tail = H.get_hyperedge_head
        hyperedge_head = H.get_hyperedge_tail
    hyperedge_weight = H.get_hyperedge_weight

    node_set = H.get_node_set()
    # Pv keeps track of the ID of the hyperedge that directely
    # preceeded each node in the traversal
    Pv = {node: None for node in node_set}

    hyperedge_ids = H.get_hyperedge_id_set()
    # W keeps track of the smallest weight path from the source node
    # to each node
    W = {node: float("inf") for node in node_set}
    W[source_node] = 0

    # k keeps track of how many nodes in the tail of each hyperedge are
    # B-connected (when all nodes in a tail are B-connected, that hyperedge
    # can then be traversed)
    k = {hyperedge_id: 0 for hyperedge_id in hyperedge_ids}

    # List of nodes removed from the priority queue in the order that
    # they were removed
    ordering = []

    Q = PriorityQueue()
    Q.add_element(W[source_node], source_node)

    while not Q.is_empty():
        # At current_node, we can traverse each hyperedge in its forward star
        current_node = Q.get_top_priority()
        ordering.append(current_node)
        for hyperedge_id in forward_star(current_node):
            # Since we're arrived at a new node, we increment
            # k[hyperedge_id] to indicate that we've reached 1 new
            # node in this hyperedge's tail
            k[hyperedge_id] += 1
            # Traverse this hyperedge only when we have reached all the nodes
            # in its tail (i.e., when k[hyperedge_id] == |T(hyperedge_id)|)
            if k[hyperedge_id] == len(hyperedge_tail(hyperedge_id)):
                f = F(hyperedge_tail(hyperedge_id), W)
                # For each node in the head of the newly-traversed hyperedge,
                # if the previous weight of the node is more than the new
                # weight...
                for head_node in \
                    [node for node in hyperedge_head(hyperedge_id) if
                     W[node] > hyperedge_weight(hyperedge_id) + f]:
                    # Update its weight to the new, smaller weight
                    W[head_node] = hyperedge_weight(hyperedge_id) + f
                    Pv[head_node] = hyperedge_id
                    # If it's not already in the priority queue...
                    if not Q.contains_element(head_node):
                        # Add it to the priority queue
                        Q.add_element(W[head_node], head_node)
                    else:
                        # Otherwise, decrease it's key in the priority queue
                        Q.reprioritize(W[head_node], head_node)

    if valid_ordering:
        return Pv, W, ordering
    else:
        return Pv, W
示例#2
0
def test_priority_queue():
    Q = PriorityQueue()

    Q.add_element(3, "a")
    Q.add_element(2, "b")
    Q.add_element(4, "c")
    Q.add_element(1, "d")
    Q.add_element(5, "e")

    assert not Q.is_empty()

    assert Q.contains_element("a")
    assert Q.contains_element("b")
    assert Q.contains_element("c")
    assert Q.contains_element("d")
    assert Q.contains_element("e")

    assert Q.peek() == "d"

    Q.reprioritize(6, "d")
    Q.reprioritize(7, "d")
    Q.reprioritize(8, "d")

    assert Q.peek() == "b"

    Q.delete_element("b")

    assert not Q.contains_element("b")
    assert Q.peek() == "a"
    assert Q.get_top_priority() == "a"
    assert not Q.contains_element("a")

    # Try invalid delete
    try:
        Q.delete_element("b")
        assert False
    except ValueError:
        pass
    except BaseException as e:
        assert False, e

    # Try invalid reprioritize
    try:
        Q.reprioritize(1, "b")
        assert False
    except ValueError:
        pass
    except BaseException as e:
        assert False, e

    Q = PriorityQueue()

    # Try invalid peek
    try:
        Q.peek()
        assert False
    except IndexError:
        pass
    except BaseException as e:
        assert False, e

    # Try invalid get_top_priority
    try:
        Q.get_top_priority()
        assert False
    except IndexError:
        pass
    except BaseException as e:
        assert False, e
示例#3
0
def _shortest_x_tree(H, source_node, b_tree,
                     F=sum_function, valid_ordering=False):
    """General form of the Shorest B-Tree algorithm, extended to also
    perform the implicit Shortest F-Tree procedure if the b_tree flag is
    not set (providing better time/memory performance than explcitily taking
    the hypergraph's symmetric image and then performing the SBT procedure
    on that).
    Uses priority queue to achieve O(size(H)*lg(n)) runtime.

    Refer to 'shorest_b_tree's or 'shorest_f_tree's documentation for
    more details.

    :param H: the H to perform the 'SXT' algorithm on.
    :param source_node: the root of the tree to be found.
    :param b_tree: boolean flag representing whether the Shortest B-Tree
                algorithm should be executed (vs the Shortest F-Tree).
    :param F: function pointer to any additive weight function; that is,
            any function that is only a function of the weights of the
            nodes in the tail of a hyperedge.
    :param valid_ordering: a boolean flag to signal whether or not a valid
                        ordering of the nodes should be returned.
    :returns:   dict -- mapping from each node to the ID of the hyperedge that
                     preceeded it in this traversal.
                dict -- mapping from each node to the node's weight.
                list -- [only if valid_ordering argument is passed] a valid
                        ordering of the nodes.
    :raises: TypeError -- Algorithm only applicable to directed hypergraphs

    """
    if not isinstance(H, DirectedHypergraph):
        raise TypeError("Algorithm only applicable to directed hypergraphs")

    if b_tree:
        forward_star = H.get_forward_star
        hyperedge_tail = H.get_hyperedge_tail
        hyperedge_head = H.get_hyperedge_head
    else:
        forward_star = H.get_backward_star
        hyperedge_tail = H.get_hyperedge_head
        hyperedge_head = H.get_hyperedge_tail
    hyperedge_weight = H.get_hyperedge_weight

    node_set = H.get_node_set()
    # Pv keeps track of the ID of the hyperedge that directely
    # preceeded each node in the traversal
    Pv = {node: None for node in node_set}

    hyperedge_ids = H.get_hyperedge_id_set()
    # W keeps track of the smallest weight path from the source node
    # to each node
    W = {node: float("inf") for node in node_set}
    W[source_node] = 0

    # k keeps track of how many nodes in the tail of each hyperedge are
    # B-connected (when all nodes in a tail are B-connected, that hyperedge
    # can then be traversed)
    k = {hyperedge_id: 0 for hyperedge_id in hyperedge_ids}

    # List of nodes removed from the priority queue in the order that
    # they were removed
    ordering = []

    Q = PriorityQueue()
    Q.add_element(W[source_node], source_node)

    while not Q.is_empty():
        # At current_node, we can traverse each hyperedge in its forward star
        current_node = Q.get_top_priority()
        ordering.append(current_node)
        for hyperedge_id in forward_star(current_node):
            # Since we're arrived at a new node, we increment
            # k[hyperedge_id] to indicate that we've reached 1 new
            # node in this hyperedge's tail
            k[hyperedge_id] += 1
            # Traverse this hyperedge only when we have reached all the nodes
            # in its tail (i.e., when k[hyperedge_id] == |T(hyperedge_id)|)
            if k[hyperedge_id] == len(hyperedge_tail(hyperedge_id)):
                f = F(hyperedge_tail(hyperedge_id), W)
                # For each node in the head of the newly-traversed hyperedge,
                # if the previous weight of the node is more than the new
                # weight...
                for head_node in \
                    [node for node in hyperedge_head(hyperedge_id) if
                     W[node] > hyperedge_weight(hyperedge_id) + f]:
                    # Update its weight to the new, smaller weight
                    W[head_node] = hyperedge_weight(hyperedge_id) + f
                    Pv[head_node] = hyperedge_id
                    # If it's not already in the priority queue...
                    if not Q.contains_element(head_node):
                        # Add it to the priority queue
                        Q.add_element(W[head_node], head_node)
                    else:
                        # Otherwise, decrease it's key in the priority queue
                        Q.reprioritize(W[head_node], head_node)

    if valid_ordering:
        return Pv, W, ordering
    else:
        return Pv, W