簡體   English   中英

如何正確匹配networkx中的邊緣屬性的子圖?

[英]How to match subgraphs with edge attributes in networkx correctly?

我是子圖匹配的新手,我發現networkx有一種非常方便的方法來做到這一點。 但是我不確定如何在具有邊緣屬性的圖上正確匹配子圖。

例如,假設我有以下具有邊緣屬性的圖表:

import networkx as nx
import networkx.algorithms.isomorphism as iso

G = nx.MultiDiGraph()
G.add_edge(1, 2, label="A")
G.add_edge(1, 2, label="D")
G.add_edge(2, 1, label="B")
G.add_edge(2, 3, label="C")

我正在嘗試找到具有以下結構的子圖:

SG = nx.MultiDiGraph()
SG.add_edge(5, 6)
SG.add_edge(6, 7)

請注意,此子圖沒有邊屬性,因此我試圖找到具有適合節點結構的任何邊的子圖(在本例中為長度為 2 的簡單路徑)。 我發現這 2 個 SO 問題( )指導我嘗試以下 2 種方法:

方法一

gm = iso.MultiDiGraphMatcher(G, SG, edge_match=iso.categorical_edge_match("label", None))
print(gm.subgraph_is_monomorphic())

for subgraph_x in gm.subgraph_monomorphisms_iter():
    print(subgraph_x)

這打印:

True
{1: 5, 2: 6, 3: 7}

但是,我假設這將打印兩個不同的子圖,因為節點 1 和 2 之間有 2 條不同的邊。意思是,這兩個子圖應該是((1,2) with edge label "A", (2,3) with edge label "C"), and ((1,2) with edge label "D", (2,3) with edge label "C") 我也找不到 output 邊緣標簽對應於 output 節點的方法,這將有助於了解此輸出的路徑。

方法二

SG = nx.MultiDiGraph()
SG.add_edge(5, 6, label="A")
SG.add_edge(6, 7, label="C")

gm = iso.MultiDiGraphMatcher(G, SG, edge_match=lambda x, y: x[0]['label'] == y[0]['label'])
print(gm.subgraph_is_monomorphic())
for subgraph_x in gm.subgraph_monomorphisms_iter():
    print(subgraph_x)

這打印

True
{1: 5, 2: 6, 3: 7}

在這里,我在子圖中指定了我想要的邊,並且 output 是有意義的,因為只有 1 個具有該結構的子圖。 例如,如果我將SG.add_edge(6, 7, label="C")更改為SG.add_edge(6, 7, label="D") ,則會打印False

但是,對於我的用例,我需要第一種方法來工作,因為我的輸入是一個沒有邊緣屬性的子圖。

我還嘗試使用沒有邊緣屬性的圖並在subgraph_isomorphisms_itersubgraph_monomorphisms_iter之間進行更改:

G = nx.MultiDiGraph([(1,2), (1,3), (1,4), (1,4), (4,5), (5,6), (1,7), (7,8)])
subgraph = nx.MultiDiGraph([(10,20), (10,30), (10,40), (40,50)])

subgraphs = []
GM = iso.MultiDiGraphMatcher(G,subgraph,edge_match=iso.categorical_edge_match([],[]))
for subgraph_x in GM.subgraph_monomorphisms_iter():
    subgraphs.append(subgraph_x)
subgraphs    

使用subgraph_monomorphisms_iter打印:

[{1: 10, 2: 20, 3: 30, 4: 40, 5: 50},
 {1: 10, 2: 20, 3: 30, 7: 40, 8: 50},
 {1: 10, 2: 20, 4: 30, 7: 40, 8: 50},
 {1: 10, 2: 20, 7: 30, 4: 40, 5: 50},
 {1: 10, 3: 20, 2: 30, 4: 40, 5: 50},
 {1: 10, 3: 20, 2: 30, 7: 40, 8: 50},
 {1: 10, 3: 20, 4: 30, 7: 40, 8: 50},
 {1: 10, 3: 20, 7: 30, 4: 40, 5: 50},
 {1: 10, 4: 20, 2: 30, 7: 40, 8: 50},
 {1: 10, 4: 20, 3: 30, 7: 40, 8: 50},
 {1: 10, 7: 20, 2: 30, 4: 40, 5: 50},
 {1: 10, 7: 20, 3: 30, 4: 40, 5: 50}]

這似乎非常多余,因為應該只有 3 個子圖,但即使使用subgraph_isomorphisms_iter似乎也能找到 2 個多余的子圖:

subgraphs = []
GM = nx.algorithms.isomorphism.DiGraphMatcher(G,subgraph) #dictionary that maps nodes of main graph to nodes of subgraph.
for subgraph_x in GM.subgraph_isomorphisms_iter():
    subgraphs.append(subgraph_x)

這打印

[{1: 10, 2: 20, 3: 30, 7: 40, 8: 50}, {1: 10, 3: 20, 2: 30, 7: 40, 8: 50}]

但是,當我嘗試將前兩種方法切換到subgraph_isomorphisms_iter時,它沒有找到任何子圖。 所以我很困惑。

更新我發現這個SO問題很好地解釋了networkx中同構和單態之間的區別。 並找到匹配器的源代碼,如果有幫助的話。 這里看來,冗余是對稱性的結果,如果我理解正確,networkx 不會考慮對稱性。

我的做法:

將 MultiDiGraph 擴展為有向圖,對擴展的有向圖執行同構檢查,這樣您就可以考慮多個邊,因為它們是單獨計算的:

import networkx as nx
import networkx.algorithms.isomorphism as iso

G = nx.MultiDiGraph()
G.add_edge(1, 2, label="A")
G.add_edge(1, 2, label="D")
G.add_edge(2, 1, label="B")
G.add_edge(2, 3, label="C")

SG = nx.MultiDiGraph()
SG.add_edge(5, 6)
SG.add_edge(6, 7)

def expand_graph(G):
    '''
    Expand the graph such that for each edge e from u to v:
    e=(u,v) you create an edge specific vertex: e -> (u,e_)(e_,u)
    This way your MultiDiGraph becomes a DiGraph
    '''
    H = nx.DiGraph()
    for u in G.nodes():
        key = 0
        for e in G.out_edges(u, data=True):
            H.add_edge(u, str(u)+'_out_'+str(key))
            H.add_edge(str(u)+'_out_'+str(key), e[1])
            key+=1
            
    nx.set_node_attributes(H, {n:0 for n in H.nodes()}, 'original')
    nx.set_node_attributes(H, {n:1 for n in G.nodes()}, 'original')
    return H

def collapse_graph(G_):
    '''
    Turns a DiGraph generated with the expand_graph function back 
    to a MultiDiGraph, adds edge attribtute label to distinguish edges
    '''
    H = nx.MultiDiGraph()
    for u, d  in G_.nodes(data=True):
        if d['original']==1:
            for u, e_ in G_.out_edges(u):
                for e_, v in G_.out_edges(e_):
                    H.add_edge(u, v, label = e_.split('_')[-1])
    return H

G_ = expand_graph(G)
SG_ = expand_graph(SG)

subgraphs = []
GM = nx.algorithms.isomorphism.DiGraphMatcher(G_,SG_) 

#Perform the check solely for isomorph subgraphs, 
#note this includes also isomorph subgraphs on the set of vertices e_
for subgraph_x in GM.subgraph_isomorphisms_iter():
    subgraphs.append(subgraph_x)

result=[]
subgraphs_valid = []

#This gets rid of subgraphs that are isomorph but not on the right set of nodes,
#i.e. we want to check whether the nodes are of mixed type (e_ mixed with u)
    
for s in subgraphs:
    if all([True if type(u)== type(v) else False for u,v in s.items()]):
        subgraphs_valid.append(s)
        
#Iterate over the isomorph subgraphs, extract the subgraphs and parse them as results
#Collapse them in order to get a edge representations with labels for the edges

for s in subgraphs_valid:  
    f = [k for k, v in s.items()]
    F_ = G_.subgraph(f)
    F = collapse_graph(F_)
    result.append(list(F.edges(data=True)))
    
print(result)

我相信有一個更簡單的解決方案......但也許這種方法可以幫助你。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM