鉴于此图:
import igraph as ig
g=ig.Graph.Erdos_Renyi(10, 0.5, directed=True)
我们可以使用 triad_census 函数轻松找到它的三合会人口普查:
tc = g.triad_census()
如果我们打印'tc',我们会得到类似的东西:
003 : -2147483648 | 012 : 5 | 102 : 2 | 021D: 4
021U: 6 | 021C: 8 | 111D: 9 | 111U: 14
030T: 5 | 030C: 3 | 201 : 6 | 120D: 9
120U: 5 | 120C: 21 | 210 : 18 | 300 : 4
也就是说,对于每种三元组类型,我们都有在图中找到它的次数。
与“三合会普查”不同,“三合会列表”不仅会给出发现三合会的次数,还会给出每次出现的参与者节点。据我所知,这里的问题是“三合会列表算法”不一定与“三合会人口普查算法”相同,后者的计算成本较低。
我尝试查看同构,定义每个三元组,然后在图中搜索它们:
# Set up the 16 possible triads
triad_dict=dict()
#003 A,B,C, the empty graph.
triad = ig.Graph(n = 3, directed=True)
triad_dict['003'] = triad
#012 A->B, C, the graph with a single directed edge.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1)])
triad_dict['012'] = triad
#102 A<->B, C, the graph with a mutual connection between two vertices.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1),
(0,2)])
triad_dict['102'] = triad
#021D A<-B->C, the out-star.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1),
(0,2)])
triad_dict['021D'] = triad
#021U A->B<-C, the in-star.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(1,0),
(2,0)])
triad_dict['021U'] = triad
#021C A->B->C, directed line.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1),
(1,2)])
triad_dict['021C'] = triad
#111D A<->B<-C.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1),
(0,2)])
triad_dict['111D'] = triad
#111U A<->B->C.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1),
(0,2),(2,0)])
triad_dict['111U'] = triad
#030T A->B<-C, A->C.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1),
(2,1),
(0,2)])
triad_dict['030T'] = triad
#030C A<-B<-C, A->C.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(1,0),
(2,1),
(0,2)])
triad_dict['030C'] = triad
#201 A<->B<->C.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1),(1,0),
(1,2),(2,1)])
triad_dict['201'] = triad
#120D A<-B->C, A<->C.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1),
(0,2),
(1,2), (2,1)])
triad_dict['120D'] = triad
#120U A->B<-C, A<->C.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1),
(2,1),
(0,2), (2,0)])
triad_dict['120U'] = triad
#120C A->B->C, A<->C.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1),
(1,2),
(0,2), (2,0)])
triad_dict['120C'] = triad
#210 A->B<->C, A<->C.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1),
(1,2), (2,1),
(0,2), (2,0)])
triad_dict['210'] = triad
#300 A<->B<->C, A<->C, the complete graph.
triad = ig.Graph(n = 3, directed=True)
triad.add_edges([(0,1), (1,0),
(1,2), (2,1),
(0,2), (2,0)])
triad_dict['300'] = triad
seq = ['300',
'210',
'120C', '120U', '120D', '201',
'030C', '030T','111U','111D',
'021C','021U','021D','102',
'012',
'003']
#Search isomorphisms for every triad
isomorphisms = dict()
for key in seq:
isomorphisms[key] = g.get_subisomorphisms_vf2(triad_dict[key])
但是,如果我们有 A<->B<->C,它将关联到几个三元组:
A<->B<->C
A->B->C
A<->B->C
etc.
我只想考虑更完整的三元组(具有更多边的那个)并丢弃它的子三元组。我们可以清理同构字典中重复的三元组,从高阶(更多边)到低阶,例如:
#Search isomorphisms for every triad
seen = []
for key in seq:
isos = isomorphisms[key]
isos = [i for i in isos if i not in seen]
isomorphisms[key] = isos
seen.extend(isos)
但是,这不是一个选项,因为该图表示节点之间的一组对话。如果发生了两次不同的对话:
A->B->C->D (1)
A->B->C (2)
(图是多边的)
脚本将删除 (1),因为它认为 (2) 是对应于 (1) 的子图,但不一定正确,因为在这种情况下它们代表不同的对话。