简体   繁体   English

Networkx:在二分图中查找仅由一组中的节点组成的所有最小切割

[英]Networkx: Find all minimal cuts consisting of only nodes from one set in a bipartite graph

In the networkx python package, is there a way to find all node cuts of minimal size consisting of only nodes from one set in a bipartite graph? 在networkx python包中,有没有办法找到最小尺寸的所有节点剪切,只包含二分图中一组的节点? For example, if the two sides of a bipartite graph are A and B, how might I go about finding all minimal node cuts consisting of nodes entirely from set B? 例如,如果二分图的两边是A和B,我怎样才能找到完全来自集合B的节点组成的所有最小节点切割? The following code I have works but it's extremely slow: 以下代码我的工作,但它非常慢:

def get_one_sided_cuts(G, A, B):
    #get all cuts that consist of nodes exclusively from B which disconnect
    #nodes from A
    one_sided_cuts = []
    seen = []

    l = list(combinations(A, 2))

    for x in l:
        s = x[0]
        t = x[1]

        cut = connectivity.minimum_st_node_cut(G, s, t)
        if set(cut).issubset(B) and (cut not in seen):
            one_sided_cuts.append(cut)
        seen.append(cut)

    #find minimum cut size
    cur_min = float("inf")
    for i in one_sided_cuts:
        if len(i) < cur_min:
            cur_min = len(i)

    one_sided_cuts = [x for x in one_sided_cuts if len(x) == cur_min]

    return one_sided_cuts

Note that this actually only checks if there is a minimal cut which, if removed, would disconnect two nodes in A only. 请注意,这实际上只检查是否存在最小切割,如果移除,将仅断开A中的两个节点。 If your solution does this (instead of finding a cut that will separate any two nodes) that's fine too. 如果您的解决方案执行此操作(而不是找到将任意两个节点分开的剪切),那也没关系。 Any ideas on how to do this more efficiently? 关于如何更有效地做到这一点的任何想法?

As stated in the comment, there are a couple of interpretations of “all node cuts of minimal size consisting of only nodes from one set in a bipartite graph”. 正如评论中所述,对“最小尺寸的所有节点切割仅包含来自二部图中的一个集合的节点”有几种解释。 It either means 它或者意味着

  1. All node cuts of minimum size when restricting cuts to be in one set of the bipartite graph, or 当将切割限制在一组二分图中时,所有节点最小尺寸切割,或
  2. All node cuts in an unconstrained sense (consisting of nodes from A or B) that happen to completely lie in B. 所有节点都在不受约束的意义上(由A或B的节点组成)完全位于B中。

From your code example you are interested in 2. According to the docs, there is a way to speed up this calculation, and from profile results it helps a bit. 从您感兴趣的代码示例2.根据文档,有一种方法可以加快计算速度,并从配置文件结果中获得一些帮助。 There are auxiliary structures built, per graph, to determine the minimum node cuts. 每个图形都有辅助结构,用于确定最小节点切割。 Each node is replaced by 2 nodes, additional directed edges are added, etc. according to the Algorithm 9 in http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf We can reuse these structures instead of reconstructing them inside a tight loop: 根据http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf中的算法9,每个节点被2个节点替换,添加额外的有向边等我们可以重用这些结构而不是重建他们在一个紧密的循环中:

Improvement for Case 2: 案例2的改进:

from networkx.algorithms.connectivity import (
    build_auxiliary_node_connectivity)
from networkx.algorithms.flow import build_residual_network

from networkx.algorithms.flow import edmonds_karp

def getone_sided_cuts_Case2(G, A, B):
    # build auxiliary networks
    H = build_auxiliary_node_connectivity(G)
    R = build_residual_network(H, 'capacity')


    # get all cutes that consist of nodes exclusively from B which disconnet
    # nodes from A
    one_sided_cuts = []
    seen           = []

    l = list(combinations(A,2))

    for x in l:
        s = x[0]
        t = x[1]

    cut = minimum_st_node_cut(G, s, t, auxiliary=H, residual=R)
    if set(cut).issubset(B):
        if cut not in seen:
            one_sided_cuts.append(cut)
    seen.append(cut)

    # Find minimum cut size
    cur_min = float('inf')
    for i in one_sided_cuts:
        if len(i) < cur_min:
            curr_min = len(i)

    one_sided_cuts = [x for x in one_sided_cuts if len(x) == cur_min]

    return one_sided_cuts

For profiling purposes, you might use the following, or one of the built-in bipartite graph generators in Networkx: 对于分析目的,您可以使用以下或Networkx中的内置二分图生成器之一:

def create_bipartite_graph(size_m, size_n, num_edges):
    G = nx.Graph()

    edge_list_0 = list(range(size_m))
    edge_list_1 = list(range(size_m,size_m+size_n))
    all_edges = []

    G.add_nodes_from(edge_list_0, bipartite=0)
    G.add_nodes_from(edge_list_1, bipartite=1)

    all_edges = list(product(edge_list_0, edge_list_1))
    num_all_edges = len(all_edges)

    edges = [all_edges[i] for i in random.sample(range(num_all_edges), num_edges)]
    G.add_edges_from(edges)

    return G, edge_list_0, edge_list_1

Using %timeit , the second version runs about 5-10% faster. 使用%timeit ,第二个版本运行速度提高约5-10%。

For Case 1, the logic is a little more involved. 对于案例1,逻辑更复杂一些。 We need to consider minimal cuts from nodes only inside B. This requires a change to minimum_st_node_cut in the following way. 我们需要考虑仅在B内部节点的最小切割。这需要以下列方式更改minimum_st_node_cut Then replace all occurences of minimum_st_node_cut to rest_minimum_st_node_cut in your solution or the Case 2 solution I gave above, noting that the new function also requires specification of the sets A , B , necessarily: 然后在您的解决方案或上面给出的案例2解决方案rest_minimum_st_node_cut minimum_st_node_cut所有出现替换为rest_minimum_st_node_cut ,注意新函数还需要指定集合AB ,必要:

def rest_build_auxiliary_node_connectivity(G,A,B):
    directed = G.is_directed()

    H = nx.DiGraph()

    for node in A:
        H.add_node('%sA' % node, id=node)
        H.add_node('%sB' % node, id=node)
        H.add_edge('%sA' % node, '%sB' % node, capacity=1)

    for node in B:
        H.add_node('%sA' % node, id=node)
        H.add_node('%sB' % node, id=node)
        H.add_edge('%sA' % node, '%sB' % node, capacity=1)        

    edges = []
    for (source, target) in G.edges():
        edges.append(('%sB' % source, '%sA' % target))
        if not directed:
            edges.append(('%sB' % target, '%sA' % source))
    H.add_edges_from(edges, capacity=1)

    return H

def rest_minimum_st_node_cut(G, A, B, s, t, auxiliary=None, residual=None, flow_func=edmonds_karp):

    if auxiliary is None:
        H = rest_build_auxiliary_node_connectivity(G, A, B)
    else:
        H = auxiliary

    if G.has_edge(s,t) or G.has_edge(t,s):
        return []
    kwargs = dict(flow_func=flow_func, residual=residual, auxiliary=H)

    for node in [x for x in A if x not in [s,t]]:
        edge = ('%sA' % node, '%sB' % node)
        num_in_edges = len(H.in_edges(edge[0]))
        H[edge[0]][edge[1]]['capacity'] = num_in_edges

    edge_cut = minimum_st_edge_cut(H, '%sB' % s, '%sA' % t,**kwargs)

    node_cut = set([n for n in [H.nodes[node]['id'] for edge in edge_cut for node in edge] if n not in A])

    return node_cut - set([s,t])

We then have, for example: 然后我们有,例如:

In [1]: G = nx.Graph()
        # A = [0,1,2,3], B = [4,5,6,7]
In [2]: G.add_edges_from([(0,4),(0,5),(1,6),(1,7),(4,1),(5,1),(6,3),(7,3)])
In [3]: minimum_st_node_cut(G, 0, 3)
           {1}
In [4]: rest_minimum_st_node_cut(G,A,B,0,3)
           {6, 7}

Finally note that the minimum_st_edge_cut() function returns [] if two nodes are adjacent. 最后请注意,如果两个节点相邻,则minimum_st_edge_cut()函数返回[] Sometimes the convention is to return a set of n-1 nodes in this case, all nodes except the source or sink. 有时,惯例是在这种情况下返回一组n-1节点,除源或接收器之外的所有节点。 Anyway, with the empty list convention, and since your original solution to Case 2 loops over node pairs in A , you will likely get [] as a return value for most configurations, unless no nodes in A are adjacent, say. 无论如何,使用空列表约定,并且由于您对Case 2的原始解决方案在A节点对上循环,您可能会将[]作为大多数配置的返回值,除非A中的节点没有相邻,例如。

EDIT 编辑

The OP encountered a problem with bipartite graphs for which the sets A, B contained a mix of integers and str types. OP遇到了二分图的问题,其中集A,B包含整数和str类型的混合。 It looks to me like the build_auxiliary_node_connectivity converts those str nodes to integers causing collisions. 在我看来, build_auxiliary_node_connectivity将这些str节点转换为导致冲突的整数。 I rewrote things above, I think that takes care of it. 我重写了上面的内容,我认为这样可以解决它。 I don't see anything in the networkx docs about this, so either use all integer nodes or use the rest_build_auxiliary_node_connectivity() thing above. 我没有在networkx文档中看到任何关于此的内容,因此要么使用所有整数节点,要么使用上面的rest_build_auxiliary_node_connectivity()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM