-
Notifications
You must be signed in to change notification settings - Fork 2
/
divide.py
162 lines (134 loc) · 3.79 KB
/
divide.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon May 1 11:59:34 2017
Divide a graph into connected subgraphs
@author: laci
"""
import math
import collections
import graph as g
from typing import List, Tuple
def _next_group(remained, chunksize, bg:g.Graph) -> List[int]:
"""Calculate a group of nodes are mostly connected.
Parameters
----------
remained: the nodes are used yet
chunksize: the size of the group
bg: the big graph to divide
Returns
-------
a list of nodes
"""
group = []
stack = collections.deque()
stack_cntr = 1
first = remained.pop()
remained.add(first)
# print(remained, bg.neighbours)
stack.append(first)
i = 0
while i < chunksize:
if stack_cntr > 0:
x = stack.popleft()
stack_cntr -= 1
if x in remained:
group.append(x)
remained.remove(x)
i += 1
for j in bg.neighbours[x]:
jj = abs(j)
if jj in remained:
stack.append(jj)
stack_cntr += 1
else:
first = remained.pop()
remained.add(first)
stack.append(first)
stack_cntr = 1
return group
def divide(K: int, bg:g.Graph) -> List[List[int]]:
"""Divide the nodes of a graph based on its edges
Parameters
----------
N: number of nodes
K: number of groups
Returns
-------
List of list of nodes"""
N = bg.size
# print("N:", N)
chunksize = int(math.ceil(N) / float(K))
remained = {i for i in bg.nodes}
result = []
for i in range(1,K):
ng = _next_group(remained, chunksize, bg)
# print("ng", ng)
result.append(ng)
# remaining nodes, i.e. are not used yet
result.append(list(remained))
# print("divide:", result)
return result
def split_edges(lower, upper, g):
"""Select the edges of the subgraph based on indices.
Parameters
----------
lower, upper: limits of calculation
g: the original graph
Returns
-------
the edges of the subgraph"""
sub_edges = []
for edge in g.edges:
x, y, v = edge
if lower <= min(x,y) and max(x,y) < upper:
sub_edges.append(edge)
return sub_edges
def filter_edges(nodes:List[int], edges: List[Tuple[int,int,int]]) -> List[Tuple[int,int,int]]:
"""Filter the edges of a subgraph.
Parameters
----------
nodes: list containing the nodes
edges: list containing the edges of the original graph
Returns
-------
inverse dictionary of nodes
filtered edges"""
ns = {v: k for k,v in enumerate(nodes)}
new_edges = []
for x, y, v in edges:
if x in ns and y in ns:
new_edges.append((x, y,v))
return new_edges
def test_divide(K,vbg):
print("Original: ", len(vbg.edges))
lgs = divide(K, vbg)
sum = 0
for ls in lgs:
ns = {v: k for k,v in enumerate(ls)}
# print(len(ls), ls)
ne = filter_edges(ns, vbg.edges)
sum += len(ne)
return sum
def test_split(K,vbg):
chunksize = int(math.ceil(vbg.size) / float(K))
sum = 0
for i in range(K):
ne = split_edges(i*chunksize, (i+1)*chunksize, vbg)
sum += len(ne)
return sum
if __name__ == "__main__":
N = 400
K = 20
vbg = g.BAGraph(N,0.7)
oe = len(vbg.edges)
de = test_divide(K,vbg)
se = test_split(K,vbg)
print("BA Original: {0}, divide: {1}({2:.1f}), split: {3}({4:.1f})".format(oe,
de, 100*de/oe, se, 100*se/oe))
vbg = g.ERGraph(N, 0.2, 0.7)
oe = len(vbg.edges)
de = test_divide(K,vbg)
se = test_split(K,vbg)
print("ER Original: {0}, divide: {1}({2:.1f}), split: {3}({4:.1f})".format(oe,
de, 100*de/oe, se, 100*se/oe))