Exemplo n.º 1
0
 def get_max_dop(G):
     """
     Get the maximum degree of parallelism of this DAG
     return : int
     """
     return max([len(antichain) for antichain in nx.antichains(G)])
     """
Exemplo n.º 2
0
 def build(self):
     anti_chain = [
         a[0] if len(a) == 1 else a for a in list(antichains(self.graph))
     ]
     anti_chain.pop(0)
     return list(
         reversed([
             a for a in anti_chain if a not in
             [c for b in anti_chain if type(b) is list for c in b]
         ]))
Exemplo n.º 3
0
 def get_max_dop(G):
     """
     Get the maximum degree of parallelism of this DAG
     return : int
     """
     max_dop = 0
     for antichain in nx.antichains(G):
         leng = len(antichain)
         if (leng > max_dop):
             max_dop = leng
     return max_dop
Exemplo n.º 4
0
 def get_max_dop(G):
     """
     Get the maximum degree of parallelism of this DAG
     return : int
     """
     max_dop = 0
     for antichain in nx.antichains(G):
         leng = len(antichain)
         if (leng > max_dop):
             max_dop = leng
     return max_dop
Exemplo n.º 5
0
    def map_path_to_element_sequence(self, edge2element, element2edge,
                                     pruned_subgraph, paths):
        element_sequences = []
        # put all path that share the same set of elements into one group
        for path in paths:
            element_sequence = []
            for i in range(len(path) - 1):
                element_sequence.append(edge2element[(path[i], path[i + 1])])
            is_added = False
            for i in range(len(element_sequences)):
                if set(element_sequences[i][0]) == set(element_sequence):
                    element_sequences[i].append(element_sequence)
                    is_added = True
                    break
            if not is_added:
                element_sequences.append([element_sequence])

        graph_with_maximum_width = DiGraph()
        width = 0
        height = 0
        # for each group, find one poset
        for ele_seq in element_sequences:
            # all pairs of elements from the first element
            linear_order = []
            for i in range(len(ele_seq[0])):
                for j in range(i + 1, len(ele_seq[0])):
                    linear_order.append((ele_seq[0][i], ele_seq[0][j]))
            # remove contradictive pairs
            for i in range(1, len(ele_seq)):
                for j in range(len(ele_seq[1]) - 1):
                    if (ele_seq[i][j + 1], ele_seq[i][j]) in linear_order:
                        linear_order.remove((ele_seq[i][j + 1], ele_seq[i][j]))
            # hasse diagram
            hasse = DiGraph()
            hasse.add_nodes_from(ele_seq[0])
            hasse.add_edges_from(linear_order)
            self.prune_subgraph(hasse)
            try:
                w = max([len(o) for o in nx.antichains(hasse)])
            except nx.exception.NetworkXUnfeasible:
                print(hasse.edges)
            # h = nx.dag_longest_path_length(hasse)
            h = len([
                e for e in hasse.nodes
                if pruned_subgraph.nodes[element2edge[e][0]]['label'] != '1'
            ])
            if w > width or (w == width and h > height):
                graph_with_maximum_width = hasse
                width = w
                height = h
        # print(height)
        return {edge for edge in graph_with_maximum_width.edges}, list(graph_with_maximum_width.nodes), \
                            graph_with_maximum_width
Exemplo n.º 6
0
 def get_max_width(G, weight='weight', default_weight=1):
     """
     Get the antichain with the maximum "weighted" width of this DAG
     weight: float (for example, it could be RAM consumption in GB)
     Return : float
     """
     max_width = 0
     for antichain in nx.antichains(G):
         t = 0
         for n in antichain:
             t += G.node[n].get(weight, default_weight)
         if (t > max_width):
             max_width = t
     return max_width
Exemplo n.º 7
0
 def get_max_width(G, weight='weight', default_weight=1):
     """
     Get the antichain with the maximum "weighted" width of this DAG
     weight: float (for example, it could be RAM consumption in GB)
     Return : float
     """
     max_width = 0
     for antichain in nx.antichains(G):
         t = 0
         for n in antichain:
             t += G.node[n].get(weight, default_weight)
         if (t > max_width):
             max_width = t
     return max_width
Exemplo n.º 8
0
def all_node_cuts(G, k=None, flow_func=None):
    r"""Returns all minimum k cutsets of an undirected graph G. 

    This implementation is based on Kanevsky's algorithm [1]_ for finding all
    minimum-size node cut-sets of an undirected graph G; ie the set (or sets) 
    of nodes of cardinality equal to the node connectivity of G. Thus if 
    removed, would break G into two or more connected components.

    Parameters
    ----------
    G : NetworkX graph
        Undirected graph

    k : Integer
        Node connectivity of the input graph. If k is None, then it is 
        computed. Default value: None.

    flow_func : function
        Function to perform the underlying flow computations. Default value
        edmonds_karp. This function performs better in sparse graphs with
        right tailed degree distributions. shortest_augmenting_path will
        perform better in denser graphs.


    Returns
    -------
    cuts : a generator of node cutsets
        Each node cutset has cardinality equal to the node connectivity of
        the input graph.

    Examples
    --------
    >>> # A two-dimensional grid graph has 4 cutsets of cardinality 2
    >>> G = nx.grid_2d_graph(5, 5)
    >>> cutsets = list(nx.all_node_cuts(G))
    >>> len(cutsets)
    4
    >>> all(2 == len(cutset) for cutset in cutsets)
    True
    >>> nx.node_connectivity(G)
    2

    Notes
    -----
    This implementation is based on the sequential algorithm for finding all
    minimum-size separating vertex sets in a graph [1]_. The main idea is to
    compute minimum cuts using local maximum flow computations among a set 
    of nodes of highest degree and all other non-adjacent nodes in the Graph.
    Once we find a minimum cut, we add an edge between the high degree
    node and the target node of the local maximum flow computation to make 
    sure that we will not find that minimum cut again.

    See also
    --------
    node_connectivity
    edmonds_karp
    shortest_augmenting_path

    References
    ----------
    .. [1]  Kanevsky, A. (1993). Finding all minimum-size separating vertex 
            sets in a graph. Networks 23(6), 533--541.
            http://onlinelibrary.wiley.com/doi/10.1002/net.3230230604/abstract

    """
    if not nx.is_connected(G):
        raise nx.NetworkXError('Input graph is disconnected.')

    # Address some corner cases first.
    # For cycle graphs
    if G.order() == G.size():
        if all(2 == d for n, d in G.degree()):
            seen = set()
            for u in G:
                for v in nx.non_neighbors(G, u):
                    if (u, v) not in seen and (v, u) not in seen:
                        yield {v, u}
                        seen.add((v, u))
            return
    # For complete Graphs
    if nx.density(G) == 1:
        for cut_set in combinations(G, len(G) - 1):
            yield set(cut_set)
        return
    # Initialize data structures.
    # Keep track of the cuts already computed so we do not repeat them.
    seen = []
    # Even-Tarjan reduction is what we call auxiliary digraph
    # for node connectivity.
    H = build_auxiliary_node_connectivity(G)
    mapping = H.graph['mapping']
    R = build_residual_network(H, 'capacity')
    kwargs = dict(capacity='capacity', residual=R)
    # Define default flow function
    if flow_func is None:
        flow_func = default_flow_func
    if flow_func is shortest_augmenting_path:
        kwargs['two_phase'] = True
    # Begin the actual algorithm
    # step 1: Find node connectivity k of G
    if k is None:
        k = nx.node_connectivity(G, flow_func=flow_func)
    # step 2:
    # Find k nodes with top degree, call it X:
    X = {n for n, d in sorted(G.degree(), key=itemgetter(1), reverse=True)[:k]}
    # Check if X is a k-node-cutset
    if _is_separating_set(G, X):
        seen.append(X)
        yield X

    for x in X:
        # step 3: Compute local connectivity flow of x with all other
        # non adjacent nodes in G
        non_adjacent = set(G) - X - set(G[x])
        for v in non_adjacent:
            # step 4: compute maximum flow in an Even-Tarjan reduction H of G
            # and step:5 build the associated residual network R
            R = flow_func(H, '%sB' % mapping[x], '%sA' % mapping[v], **kwargs)
            flow_value = R.graph['flow_value']

            if flow_value == k:
                # Remove saturated edges form the residual network
                saturated_edges = [(u, w, d) for (u, w, d) in
                                   R.edges(data=True)
                                   if d['capacity'] == d['flow']]
                R.remove_edges_from(saturated_edges)
                # step 6: shrink the strongly connected components of
                # residual flow network R and call it L
                L = nx.condensation(R)
                cmap = L.graph['mapping']
                # step 7: Compute antichains of L; they map to closed sets in H
                # Any edge in H that links a closed set is part of a cutset
                for antichain in nx.antichains(L):
                    # Nodes in an antichain of the condensation graph of
                    # the residual network map to a closed set of nodes that
                    # define a node partition of the auxiliary digraph H.
                    S = {n for n, scc in cmap.items() if scc in antichain}
                    # Find the cutset that links the node partition (S,~S) in H
                    cutset = set()
                    for u in S:
                        cutset.update((u, w) for w in H[u] if w not in S)
                    # The edges in H that form the cutset are internal edges
                    # (ie edges that represent a node of the original graph G)
                    node_cut = {H.nodes[n]['id'] for edge in cutset for n in edge}

                    if len(node_cut) == k:
                        if node_cut not in seen:
                            yield node_cut
                            seen.append(node_cut)
                        # Add an edge (x, v) to make sure that we do not
                        # find this cutset again. This is equivalent
                        # of adding the edge in the input graph
                        # G.add_edge(x, v) and then regenerate H and R:
                        # Add edges to the auxiliary digraph.
                        H.add_edge('%sB' % mapping[x], '%sA' % mapping[v],
                                   capacity=1)
                        H.add_edge('%sB' % mapping[v], '%sA' % mapping[x],
                                   capacity=1)
                        # Add edges to the residual network.
                        R.add_edge('%sB' % mapping[x], '%sA' % mapping[v],
                                   capacity=1)
                        R.add_edge('%sA' % mapping[v], '%sB' % mapping[x],
                                   capacity=1)
                        break
                # Add again the saturated edges to reuse the residual network
                R.add_edges_from(saturated_edges)
Exemplo n.º 9
0
def all_node_cuts(G, k=None, flow_func=None):
    r"""Returns all minimum k cutsets of an undirected graph G. 

    This implementation is based on Kanevsky's algorithm [1]_ for finding all
    minimum-size node cut-sets of an undirected graph G; ie the set (or sets) 
    of nodes of cardinality equal to the node connectivity of G. Thus if 
    removed, would break G into two or more connected components.
   
    Parameters
    ----------
    G : NetworkX graph
        Undirected graph

    k : Integer
        Node connectivity of the input graph. If k is None, then it is 
        computed. Default value: None.

    flow_func : function
        Function to perform the underlying flow computations. Default value
        edmonds_karp. This function performs better in sparse graphs with
        right tailed degree distributions. shortest_augmenting_path will
        perform better in denser graphs.
        

    Returns
    -------
    cuts : a generator of node cutsets
        Each node cutset has cardinality equal to the node connectivity of
        the input graph.

    Examples
    --------
    >>> # A two-dimensional grid graph has 4 cutsets of cardinality 2
    >>> G = nx.grid_2d_graph(5, 5)
    >>> cutsets = list(nx.all_node_cuts(G))
    >>> len(cutsets)
    4
    >>> all(2 == len(cutset) for cutset in cutsets)
    True
    >>> nx.node_connectivity(G)
    2

    Notes
    -----
    This implementation is based on the sequential algorithm for finding all
    minimum-size separating vertex sets in a graph [1]_. The main idea is to
    compute minimum cuts using local maximum flow computations among a set 
    of nodes of highest degree and all other non-adjacent nodes in the Graph.
    Once we find a minimum cut, we add an edge between the high degree
    node and the target node of the local maximum flow computation to make 
    sure that we will not find that minimum cut again.

    See also
    --------
    node_connectivity
    edmonds_karp
    shortest_augmenting_path

    References
    ----------
    .. [1]  Kanevsky, A. (1993). Finding all minimum-size separating vertex 
            sets in a graph. Networks 23(6), 533--541.
            http://onlinelibrary.wiley.com/doi/10.1002/net.3230230604/abstract

    """
    if not nx.is_connected(G):
        raise nx.NetworkXError('Input graph is disconnected.')

    # Addess some corner cases first.
    # For cycle graphs
    if G.order() == G.size():
        if all(2 == d for n, d in G.degree()):
            seen = set()
            for u in G:
                for v in nx.non_neighbors(G, u):
                    if (u, v) not in seen and (v, u) not in seen:
                        yield {v, u}
                        seen.add((v, u))
            return
    # For complete Graphs
    if nx.density(G) == 1:
        for cut_set in combinations(G, len(G) - 1):
            yield set(cut_set)
        return
    # Initialize data structures.
    # Keep track of the cuts already computed so we do not repeat them.
    seen = []
    # Even-Tarjan reduction is what we call auxiliary digraph
    # for node connectivity.
    H = build_auxiliary_node_connectivity(G)
    mapping = H.graph['mapping']
    R = build_residual_network(H, 'capacity')
    kwargs = dict(capacity='capacity', residual=R)
    # Define default flow function
    if flow_func is None:
        flow_func = default_flow_func
    if flow_func is shortest_augmenting_path:
        kwargs['two_phase'] = True
    # Begin the actual algorithm
    # step 1: Find node connectivity k of G
    if k is None:
        k = nx.node_connectivity(G, flow_func=flow_func)
    # step 2:
    # Find k nodes with top degree, call it X:
    X = {n for n, d in sorted(G.degree(), key=itemgetter(1), reverse=True)[:k]}
    # Check if X is a k-node-cutset
    if _is_separating_set(G, X):
        seen.append(X)
        yield X

    for x in X:
        # step 3: Compute local connectivity flow of x with all other
        # non adjacent nodes in G
        non_adjacent = set(G) - X - set(G[x])
        for v in non_adjacent:
            # step 4: compute maximum flow in an Even-Tarjan reduction H of G
            # and step:5 build the associated residual network R
            R = flow_func(H, '%sB' % mapping[x], '%sA' % mapping[v], **kwargs)
            flow_value = R.graph['flow_value']

            if flow_value == k:
                ## Remove saturated edges form the residual network
                saturated_edges = [(u, w, d)
                                   for (u, w, d) in R.edges(data=True)
                                   if d['capacity'] == d['flow']]
                R.remove_edges_from(saturated_edges)
                # step 6: shrink the strongly connected components of
                # residual flow network R and call it L
                L = nx.condensation(R)
                cmap = L.graph['mapping']
                # step 7: Compute antichains of L; they map to closed sets in H
                # Any edge in H that links a closed set is part of a cutset
                for antichain in nx.antichains(L):
                    # Nodes in an antichain of the condensation graph of
                    # the residual network map to a closed set of nodes that
                    # define a node partition of the auxiliary digraph H.
                    S = {n for n, scc in cmap.items() if scc in antichain}
                    # Find the cutset that links the node partition (S,~S) in H
                    cutset = set()
                    for u in S:
                        cutset.update((u, w) for w in H[u] if w not in S)
                    # The edges in H that form the cutset are internal edges
                    # (ie edges that represent a node of the original graph G)
                    node_cut = {
                        H.node[n]['id']
                        for edge in cutset for n in edge
                    }

                    if len(node_cut) == k:
                        if node_cut not in seen:
                            yield node_cut
                            seen.append(node_cut)
                        # Add an edge (x, v) to make sure that we do not
                        # find this cutset again. This is equivalent
                        # of adding the edge in the input graph
                        # G.add_edge(x, v) and then regenerate H and R:
                        # Add edges to the auxiliary digraph.
                        H.add_edge('%sB' % mapping[x],
                                   '%sA' % mapping[v],
                                   capacity=1)
                        H.add_edge('%sB' % mapping[v],
                                   '%sA' % mapping[x],
                                   capacity=1)
                        # Add edges to the residual network.
                        R.add_edge('%sB' % mapping[x],
                                   '%sA' % mapping[v],
                                   capacity=1)
                        R.add_edge('%sA' % mapping[v],
                                   '%sB' % mapping[x],
                                   capacity=1)
                        break
                # Add again the saturated edges to reuse the residual network
                R.add_edges_from(saturated_edges)
Exemplo n.º 10
0
def all_node_cuts(G, k=None, flow_func=None):
    r"""Returns all minimum k cutsets of an undirected graph G.

    This implementation is based on Kanevsky's algorithm [1]_ for finding all
    minimum-size node cut-sets of an undirected graph G; ie the set (or sets)
    of nodes of cardinality equal to the node connectivity of G. Thus if
    removed, would break G into two or more connected components.

    Parameters
    ----------
    G : NetworkX graph
        Undirected graph

    k : Integer
        Node connectivity of the input graph. If k is None, then it is
        computed. Default value: None.

    flow_func : function
        Function to perform the underlying flow computations. Default value
        edmonds_karp. This function performs better in sparse graphs with
        right tailed degree distributions. shortest_augmenting_path will
        perform better in denser graphs.


    Returns
    -------
    cuts : a generator of node cutsets
        Each node cutset has cardinality equal to the node connectivity of
        the input graph.

    Examples
    --------
    >>> # A two-dimensional grid graph has 4 cutsets of cardinality 2
    >>> G = nx.grid_2d_graph(5, 5)
    >>> cutsets = list(nx.all_node_cuts(G))
    >>> len(cutsets)
    4
    >>> all(2 == len(cutset) for cutset in cutsets)
    True
    >>> nx.node_connectivity(G)
    2

    Notes
    -----
    This implementation is based on the sequential algorithm for finding all
    minimum-size separating vertex sets in a graph [1]_. The main idea is to
    compute minimum cuts using local maximum flow computations among a set
    of nodes of highest degree and all other non-adjacent nodes in the Graph.
    Once we find a minimum cut, we add an edge between the high degree
    node and the target node of the local maximum flow computation to make
    sure that we will not find that minimum cut again.

    See also
    --------
    node_connectivity
    edmonds_karp
    shortest_augmenting_path

    References
    ----------
    .. [1]  Kanevsky, A. (1993). Finding all minimum-size separating vertex
            sets in a graph. Networks 23(6), 533--541.
            http://onlinelibrary.wiley.com/doi/10.1002/net.3230230604/abstract

    """
    if not nx.is_connected(G):
        raise nx.NetworkXError("Input graph is disconnected.")

    # Address some corner cases first.
    # For complete Graphs
    if nx.density(G) == 1:
        for cut_set in combinations(G, len(G) - 1):
            yield set(cut_set)
        return
    # Initialize data structures.
    # Keep track of the cuts already computed so we do not repeat them.
    seen = []
    # Even-Tarjan reduction is what we call auxiliary digraph
    # for node connectivity.
    H = build_auxiliary_node_connectivity(G)
    H_nodes = H.nodes  # for speed
    mapping = H.graph["mapping"]
    # Keep a copy of original predecessors, H will be modified later.
    # Shallow copy is enough.
    original_H_pred = copy.copy(H._pred)
    R = build_residual_network(H, "capacity")
    kwargs = dict(capacity="capacity", residual=R)
    # Define default flow function
    if flow_func is None:
        flow_func = default_flow_func
    if flow_func is shortest_augmenting_path:
        kwargs["two_phase"] = True
    # Begin the actual algorithm
    # step 1: Find node connectivity k of G
    if k is None:
        k = nx.node_connectivity(G, flow_func=flow_func)
    # step 2:
    # Find k nodes with top degree, call it X:
    X = {n for n, d in sorted(G.degree(), key=itemgetter(1), reverse=True)[:k]}
    # Check if X is a k-node-cutset
    if _is_separating_set(G, X):
        seen.append(X)
        yield X

    for x in X:
        # step 3: Compute local connectivity flow of x with all other
        # non adjacent nodes in G
        non_adjacent = set(G) - X - set(G[x])
        for v in non_adjacent:
            # step 4: compute maximum flow in an Even-Tarjan reduction H of G
            # and step 5: build the associated residual network R
            R = flow_func(H, f"{mapping[x]}B", f"{mapping[v]}A", **kwargs)
            flow_value = R.graph["flow_value"]

            if flow_value == k:
                # Find the nodes incident to the flow.
                E1 = flowed_edges = [(u, w) for (u, w, d) in R.edges(data=True)
                                     if d["flow"] != 0]
                VE1 = incident_nodes = {n for edge in E1 for n in edge}
                # Remove saturated edges form the residual network.
                # Note that reversed edges are introduced with capacity 0
                # in the residual graph and they need to be removed too.
                saturated_edges = [
                    (u, w, d) for (u, w, d) in R.edges(data=True)
                    if d["capacity"] == d["flow"] or d["capacity"] == 0
                ]
                R.remove_edges_from(saturated_edges)
                R_closure = nx.transitive_closure(R)
                # step 6: shrink the strongly connected components of
                # residual flow network R and call it L.
                L = nx.condensation(R)
                cmap = L.graph["mapping"]
                inv_cmap = defaultdict(list)
                for n, scc in cmap.items():
                    inv_cmap[scc].append(n)
                # Find the incident nodes in the condensed graph.
                VE1 = {cmap[n] for n in VE1}
                # step 7: Compute all antichains of L;
                # they map to closed sets in H.
                # Any edge in H that links a closed set is part of a cutset.
                for antichain in nx.antichains(L):
                    # Only antichains that are subsets of incident nodes counts.
                    # Lemma 8 in reference.
                    if not set(antichain).issubset(VE1):
                        continue
                    # Nodes in an antichain of the condensation graph of
                    # the residual network map to a closed set of nodes that
                    # define a node partition of the auxiliary digraph H
                    # through taking all of antichain's predecessors in the
                    # transitive closure.
                    S = set()
                    for scc in antichain:
                        S.update(inv_cmap[scc])
                    S_ancestors = set()
                    for n in S:
                        S_ancestors.update(R_closure._pred[n])
                    S.update(S_ancestors)
                    if f"{mapping[x]}B" not in S or f"{mapping[v]}A" in S:
                        continue
                    # Find the cutset that links the node partition (S,~S) in H
                    cutset = set()
                    for u in S:
                        cutset.update(
                            (u, w) for w in original_H_pred[u] if w not in S)
                    # The edges in H that form the cutset are internal edges
                    # (ie edges that represent a node of the original graph G)
                    if any([
                            H_nodes[u]["id"] != H_nodes[w]["id"]
                            for u, w in cutset
                    ]):
                        continue
                    node_cut = {H_nodes[u]["id"] for u, _ in cutset}

                    if len(node_cut) == k:
                        # The cut is invalid if it includes internal edges of
                        # end nodes. The other half of Lemma 8 in ref.
                        if x in node_cut or v in node_cut:
                            continue
                        if node_cut not in seen:
                            yield node_cut
                            seen.append(node_cut)

                # Add an edge (x, v) to make sure that we do not
                # find this cutset again. This is equivalent
                # of adding the edge in the input graph
                # G.add_edge(x, v) and then regenerate H and R:
                # Add edges to the auxiliary digraph.
                # See build_residual_network for convention we used
                # in residual graphs.
                H.add_edge(f"{mapping[x]}B", f"{mapping[v]}A", capacity=1)
                H.add_edge(f"{mapping[v]}B", f"{mapping[x]}A", capacity=1)
                # Add edges to the residual network.
                R.add_edge(f"{mapping[x]}B", f"{mapping[v]}A", capacity=1)
                R.add_edge(f"{mapping[v]}A", f"{mapping[x]}B", capacity=0)
                R.add_edge(f"{mapping[v]}B", f"{mapping[x]}A", capacity=1)
                R.add_edge(f"{mapping[x]}A", f"{mapping[v]}B", capacity=0)

                # Add again the saturated edges to reuse the residual network
                R.add_edges_from(saturated_edges)
Exemplo n.º 11
0
def get_parallelism(G):
    return max(map(lambda x: len(x), list(nx.antichains(G))))
Exemplo n.º 12
0
 def get_max_antichains(G):
     """
     return a list of antichains with Top-3 lengths
     """
     return DAGUtil.prune_antichains(nx.antichains(G))
Exemplo n.º 13
0
 def get_max_antichains(G):
     """
     return a list of antichains with Top-3 lengths
     """
     return DAGUtil.prune_antichains(nx.antichains(G))