Skip to content

Commit c2ba02d

Browse files
authored
Merge pull request #170 from Musicaldg/pybind11
Refactor community detection for unified parallel implementation
2 parents 7f7af74 + 2d1ae79 commit c2ba02d

2 files changed

Lines changed: 200 additions & 85 deletions

File tree

cpp_easygraph/functions/community/localsearch.cpp

Lines changed: 199 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,60 @@ struct RootDecision {
2222
int degree;
2323
};
2424

25+
int choose_center(const vector<pair<int, double>>& sorted_multi) {
26+
if (sorted_multi.size() < 2) {
27+
return 1;
28+
}
29+
30+
vector<double> y;
31+
for (const auto& p : sorted_multi) {
32+
y.push_back(p.second);
33+
}
34+
35+
vector<double> delta;
36+
for (size_t i = 1; i < y.size(); ++i) {
37+
delta.push_back(fabs(y[i] - y[i-1]));
38+
}
39+
40+
if (delta.empty()) {
41+
return 1;
42+
}
43+
44+
vector<double> delta_nozero;
45+
for (double d : delta) {
46+
if (d != 0) {
47+
delta_nozero.push_back(d);
48+
}
49+
}
50+
51+
if (delta_nozero.empty()) {
52+
return 1;
53+
}
54+
55+
double mean = 0;
56+
for (double d : delta_nozero) {
57+
mean += d;
58+
}
59+
mean /= delta_nozero.size();
60+
61+
double variance = 0;
62+
for (double d : delta_nozero) {
63+
variance += (d - mean) * (d - mean);
64+
}
65+
variance /= delta_nozero.size();
66+
double std_dev = sqrt(variance);
67+
68+
double threshold = std_dev + mean;
69+
70+
for (size_t i = 0; i < delta.size(); ++i) {
71+
if (delta[i] > threshold) {
72+
return i + 1;
73+
}
74+
}
75+
76+
return 0;
77+
}
78+
2579
py::object cpp_localsearch(
2680
py::object G,
2781
py::object center_num,
@@ -40,6 +94,30 @@ py::object cpp_localsearch(
4094
return py::make_tuple(py::none(), py::list(), py::list(), py::dict(), py::dict(), py::none());
4195
}
4296

97+
bool has_edges = false;
98+
for (int u = 1; u <= n; ++u) {
99+
if (GL.head[u] != -1) {
100+
has_edges = true;
101+
break;
102+
}
103+
}
104+
if (!has_edges) {
105+
py::dict result_grouped;
106+
py::list result_center_dcd;
107+
py::list result_y_dcd;
108+
py::dict result_y_partition;
109+
for (int u = 1; u <= n; ++u) {
110+
py::object node = id_to_node[py::cast(u)];
111+
py::list members;
112+
members.append(node);
113+
result_grouped[node] = members;
114+
result_center_dcd.append(node);
115+
result_y_dcd.append(node);
116+
result_y_partition[node] = node;
117+
}
118+
return py::make_tuple(py::none(), result_center_dcd, result_y_dcd, result_y_partition, result_grouped, py::none());
119+
}
120+
43121
std::mt19937 rng(42);
44122
if (!seed.is_none()) {
45123
try {
@@ -73,6 +151,7 @@ py::object cpp_localsearch(
73151
unordered_map<int, vector<int>> dag_pred;
74152

75153
for (int v = 1; v <= n; ++v) {
154+
if (degree[v] == 0) continue;
76155
int kv = degree[v];
77156
vector<pair<int, int>> neighbors;
78157
for (int e = GL.head[v]; e != -1; e = GL.edges[e].next) {
@@ -125,11 +204,43 @@ py::object cpp_localsearch(
125204

126205
vector<int> roots;
127206
for (int u = 1; u <= n; ++u) {
128-
if (out_degree_dag[u] == 0) {
207+
if (out_degree_dag[u] == 0 && degree[u] > 0) {
129208
roots.push_back(u);
130209
}
131210
}
132211

212+
if (roots.empty()) {
213+
for (int u = 1; u <= n; ++u) {
214+
if (degree[u] > 0) {
215+
roots.push_back(u);
216+
}
217+
}
218+
}
219+
220+
if (roots.size() > 1) {
221+
bool all_same_degree = true;
222+
int first_degree = -1;
223+
for (int root : roots) {
224+
if (first_degree == -1) {
225+
first_degree = degree[root];
226+
} else if (degree[root] != first_degree) {
227+
all_same_degree = false;
228+
break;
229+
}
230+
}
231+
232+
if (all_same_degree) {
233+
int max_root = -1;
234+
for (int root : roots) {
235+
if (root > max_root) {
236+
max_root = root;
237+
}
238+
}
239+
roots.clear();
240+
roots.push_back(max_root);
241+
}
242+
}
243+
133244
unordered_map<int, int> tree_rootnode;
134245
unordered_map<int, int> tree_parentnode;
135246
unordered_map<int, int> tree_distancetoroot;
@@ -200,30 +311,25 @@ py::object cpp_localsearch(
200311
unordered_set<int> root_set(valid_roots.begin(), valid_roots.end());
201312
unordered_map<int, RootDecision> root_decision;
202313

203-
for (int root : valid_roots) {
314+
auto BFS_from_s = [&](int s) -> pair<int, int> {
204315
queue<int> search_queue;
205316
unordered_map<int, int> path_dict;
206317
unordered_set<int> seen;
207318

208-
search_queue.push(root);
209-
seen.insert(root);
210-
path_dict[root] = 0;
211-
212-
int superior = root;
213-
int shortest_path = -1;
319+
search_queue.push(s);
320+
seen.insert(s);
321+
path_dict[s] = 0;
214322

215-
while (!search_queue.empty() && shortest_path == -1) {
323+
while (!search_queue.empty()) {
216324
int vertex = search_queue.front();
217325
search_queue.pop();
218326
int current_dist = path_dict[vertex];
219327

220328
vector<pair<int, int>> neighbors;
221329
for (int e = GL.head[vertex]; e != -1; e = GL.edges[e].next) {
222330
int nn = GL.edges[e].to;
223-
if (!seen.count(nn)) {
224-
int deg_nn = degree[nn];
225-
neighbors.push_back({nn, deg_nn});
226-
}
331+
int deg_nn = degree[nn];
332+
neighbors.push_back({nn, deg_nn});
227333
}
228334

229335
sort(neighbors.begin(), neighbors.end(),
@@ -239,18 +345,20 @@ py::object cpp_localsearch(
239345
search_queue.push(w);
240346
}
241347

242-
if (root_set.count(w) && degree[w] > degree[root]) {
243-
superior = w;
244-
shortest_path = path_dict[w];
245-
break;
348+
if (root_set.count(w) && degree[w] > degree[s]) {
349+
return {w, path_dict[w]};
246350
}
247351
}
248352
}
249-
250-
root_decision[root] = {superior, shortest_path, degree[root]};
353+
return {s, -1};
354+
};
355+
356+
for (int root : valid_roots) {
357+
auto result = BFS_from_s(root);
358+
root_decision[root] = {result.first, result.second, degree[root]};
251359
}
252360

253-
int max_path = 0;
361+
int max_path = -1;
254362
for (auto& kv : root_decision) {
255363
if (kv.second.path_length > max_path) {
256364
max_path = kv.second.path_length;
@@ -266,7 +374,7 @@ py::object cpp_localsearch(
266374

267375
unordered_map<int, RootDecision> node_plot = root_decision;
268376
for (int node = 1; node <= n; ++node) {
269-
if (node_plot.find(node) == node_plot.end()) {
377+
if (node_plot.find(node) == node_plot.end() && degree[node] > 0) {
270378
int parent = tree_parentnode[node];
271379
node_plot[node] = {parent, 1, degree[node]};
272380
}
@@ -304,6 +412,8 @@ py::object cpp_localsearch(
304412
degree_rank[p.second] = rank;
305413
rank++;
306414
last_deg = p.first;
415+
} else {
416+
degree_rank[p.second] = rank - 1;
307417
}
308418
}
309419

@@ -326,8 +436,20 @@ py::object cpp_localsearch(
326436
unordered_map<int, double> multi_dict;
327437
for (size_t i = 0; i < node_ids.size(); ++i) {
328438
int node = node_ids[i];
329-
double norm_deg = (double)(degree_rank[node] - min_rank) / rank_range;
330-
double norm_sq_path = (square_path[i] - min_sq_path) / sq_range;
439+
double norm_deg, norm_sq_path;
440+
441+
if (max_rank == min_rank) {
442+
norm_deg = 1.0 / (double)node_ids.size();
443+
} else {
444+
norm_deg = (double)(degree_rank[node] - min_rank) / rank_range;
445+
}
446+
447+
if (max_sq_path == min_sq_path) {
448+
norm_sq_path = 1.0 / (double)node_ids.size();
449+
} else {
450+
norm_sq_path = (square_path[i] - min_sq_path) / sq_range;
451+
}
452+
331453
multi_dict[node] = norm_deg * norm_sq_path;
332454
}
333455

@@ -338,14 +460,28 @@ py::object cpp_localsearch(
338460
sort(sorted_multi.begin(), sorted_multi.end(),
339461
[](const pair<int, double>& a, const pair<int, double>& b) {
340462
if (fabs(a.second - b.second) > 1e-9) return a.second > b.second;
341-
return a.first < b.first;
463+
return a.first > b.first;
342464
});
343465

344466
int num_centers = (int)valid_roots.size();
345-
if (!center_num.is_none()) {
467+
468+
bool auto_choose = auto_choose_centers.cast<bool>();
469+
if (auto_choose && sorted_multi.size() > 0) {
470+
int auto_centernum = choose_center(sorted_multi);
471+
if (!center_num.is_none()) {
472+
int user_center_num = center_num.cast<int>();
473+
num_centers = (auto_centernum < user_center_num) ? auto_centernum : user_center_num;
474+
} else {
475+
num_centers = auto_centernum;
476+
}
477+
} else if (!center_num.is_none()) {
346478
num_centers = center_num.cast<int>();
347479
}
348480

481+
if (num_centers <= 0) {
482+
num_centers = (int)valid_roots.size();
483+
}
484+
349485
vector<int> center_dcd;
350486
int local_cnt = 0;
351487
for (size_t i = 0; i < sorted_multi.size() && local_cnt < num_centers; ++i) {
@@ -355,6 +491,27 @@ py::object cpp_localsearch(
355491
}
356492
}
357493

494+
if (center_dcd.empty() && !sorted_multi.empty()) {
495+
center_dcd.push_back(sorted_multi[0].first);
496+
}
497+
498+
bool all_same_degree = true;
499+
int first_deg = -1;
500+
for (int i = 1; i <= n && all_same_degree; ++i) {
501+
if (degree[i] > 0) {
502+
if (first_deg == -1) {
503+
first_deg = degree[i];
504+
} else if (degree[i] != first_deg) {
505+
all_same_degree = false;
506+
}
507+
}
508+
}
509+
510+
if (all_same_degree && n > 0) {
511+
center_dcd.clear();
512+
center_dcd.push_back(n);
513+
}
514+
358515
unordered_set<int> center_set(center_dcd.begin(), center_dcd.end());
359516

360517
for (int node : valid_roots) {
@@ -363,35 +520,40 @@ py::object cpp_localsearch(
363520
tree_rootnode[node] = superior;
364521
}
365522

366-
for (int node = 1; node <= n; ++node) {
367-
if (center_set.count(node)) {
523+
for (int node = 0; node < n; ++node) {
524+
if (degree[node] > 0 && center_set.count(node)) {
368525
tree_rootnode[node] = node;
369526
}
370527
}
371528

372-
for (int node = 1; node <= n; ++node) {
373-
int current = node;
529+
for (int node = 0; node < n; ++node) {
530+
if (degree[node] == 0) continue;
374531
vector<int> recent;
375-
recent.push_back(current);
532+
recent.push_back(node);
376533
bool flag = false;
377534

378-
while (center_set.find(tree_rootnode[current]) == center_set.end() && !flag) {
379-
int next = tree_rootnode[current];
380-
if (next == -1 || find(recent.begin(), recent.end(), next) != recent.end()) {
381-
tree_rootnode[current] = -1;
535+
while (center_set.find(tree_rootnode[node]) == center_set.end() && !flag) {
536+
int j = tree_rootnode[node];
537+
if (j == -1 || find(recent.begin(), recent.end(), j) != recent.end()) {
538+
tree_rootnode[node] = -1;
382539
flag = true;
383540
break;
384541
}
385-
recent.push_back(next);
386-
tree_rootnode[current] = tree_rootnode[next];
387-
current = next;
542+
recent.push_back(j);
543+
tree_rootnode[node] = tree_rootnode[j];
388544
}
389545
}
390546

391547
unordered_map<int, int> y_partition;
392548
vector<int> y_dcd;
549+
393550
for (int node = 1; node <= n; ++node) {
551+
if (degree[node] == 0) continue;
394552
int root = tree_rootnode[node];
553+
if (root == -1 && !center_dcd.empty()) {
554+
root = center_dcd[0];
555+
tree_rootnode[node] = root;
556+
}
395557
y_partition[node] = root;
396558
if (root == -1) {
397559
y_dcd.push_back(-1);

0 commit comments

Comments
 (0)