Skip to content

Commit cc67d86

Browse files
committed
🐛 fix disconnected communities (fixes #2)
Ensure that the aggregated graph is undirected. Since we are use low-memory scans for aggregate, the aggregated graph is not guaranteed to be undirected by default, even in the input graph is undirected.
1 parent 20f9fe8 commit cc67d86

File tree

3 files changed

+65
-7
lines changed

3 files changed

+65
-7
lines changed

inc/csr.hxx

+55-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,47 @@ inline K csrDegree(const vector<O>& offsets, K u) {
2525

2626

2727

28+
#pragma region HAS EDGE
29+
/**
30+
* Check if an edge exists in the graph.
31+
* @param offsets offsets of the outgoing edges of vertices
32+
* @param edgeKeys vertex ids of the outgoing edges of each vertex
33+
* @param u source vertex id
34+
* @param v target vertex id
35+
* @returns true if edge exists, false otherwise
36+
*/
37+
template <class O, class K>
38+
inline bool csrHasEdge(const vector<O>& offsets, const vector<K>& edgeKeys, K u, K v) {
39+
O i = offsets[u];
40+
O I = offsets[u+1];
41+
for (; i<I; ++i)
42+
if (edgeKeys[i]==v) return true;
43+
return false;
44+
}
45+
46+
47+
/**
48+
* Check if an edge exists in the graph.
49+
* @param offsets offsets of the outgoing edges of vertices
50+
* @param degrees degree of each vertex
51+
* @param edgeKeys vertex ids of the outgoing edges of each vertex
52+
* @param u source vertex id
53+
* @param v target vertex id
54+
* @returns true if edge exists, false otherwise
55+
*/
56+
template <class O, class K>
57+
inline bool csrHasEdge(const vector<O>& offsets, const vector<K>& degrees, const vector<K>& edgeKeys, K u, K v) {
58+
O i = offsets[u];
59+
O I = offsets[u] + degrees[u];
60+
for (; i<I; ++i)
61+
if (edgeKeys[i]==v) return true;
62+
return false;
63+
}
64+
#pragma endregion
65+
66+
67+
68+
2869
#pragma region FOREACH
2970
/**
3071
* Iterate over the target vertex ids of a source vertex in the graph.
@@ -340,15 +381,17 @@ inline void csrClearOmpW(vector<O>& offsets, vector<K>& degrees) {
340381

341382
/**
342383
* Add an edge to the graph.
384+
* @tparam CHECK check if edge already exists?
343385
* @param degrees degree of each vertex
344386
* @param edgeKeys vertex ids of the outgoing edges of each vertex
345387
* @param offsets offsets of the outgoing edges of vertices
346388
* @param u source vertex id
347389
* @param v target vertex id
348390
* @note Does not check if the edge already exists, or is there is available space.
349391
*/
350-
template <class O, class K>
392+
template <bool CHECK=false, class O, class K>
351393
inline void csrAddEdgeU(vector<K>& degrees, vector<K>& edgeKeys, const vector<O>& offsets, K u, K v) {
394+
if (CHECK && csrHasEdge(offsets, degrees, edgeKeys, u, v)) return;
352395
O n = degrees[u]++;
353396
O i = offsets[u] + n;
354397
edgeKeys[i] = v;
@@ -357,26 +400,30 @@ inline void csrAddEdgeU(vector<K>& degrees, vector<K>& edgeKeys, const vector<O>
357400
#ifdef OPENMP
358401
/**
359402
* Add an edge to the graph.
403+
* @tparam CHECK check if edge already exists?
360404
* @param degrees degree of each vertex
361405
* @param edgeKeys vertex ids of the outgoing edges of each vertex
362406
* @param offsets offsets of the outgoing edges of vertices
363407
* @param u source vertex id
364408
* @param v target vertex id
365409
* @note Does not check if the edge already exists, or is there is available space.
366410
*/
367-
template <class O, class K>
411+
template <bool CHECK=false, class O, class K>
368412
inline void csrAddEdgeOmpU(vector<K>& degrees, vector<K>& edgeKeys, const vector<O>& offsets, K u, K v) {
413+
if (CHECK && csrHasEdge(offsets, degrees, edgeKeys, u, v)) return;
369414
O n = 0;
370415
#pragma omp atomic capture
371416
{ n = degrees[u]; ++degrees[u]; }
372417
O i = offsets[u] + n;
418+
if (CHECK && offsets[u+1]<=i) return; // Check if there is available space.
373419
edgeKeys[i] = v;
374420
}
375421
#endif
376422

377423

378424
/**
379425
* Add a weighted edge to the graph.
426+
* @tparam CHECK check if edge already exists?
380427
* @param degrees degree of each vertex
381428
* @param edgeKeys vertex ids of the outgoing edges of each vertex
382429
* @param edgeValues edge values of the outgoing edges of each vertex
@@ -386,8 +433,9 @@ inline void csrAddEdgeOmpU(vector<K>& degrees, vector<K>& edgeKeys, const vector
386433
* @param w associated weight of the edge
387434
* @note Does not check if the edge already exists, or is there is available space.
388435
*/
389-
template <class O, class K, class E>
436+
template <bool CHECK=false, class O, class K, class E>
390437
inline void csrAddEdgeU(vector<K>& degrees, vector<K>& edgeKeys, vector<E>& edgeValues, const vector<O>& offsets, K u, K v, E w) {
438+
if (CHECK && csrHasEdge(offsets, degrees, edgeKeys, u, v)) return;
391439
O n = degrees[u]++;
392440
O i = offsets[u] + n;
393441
edgeKeys[i] = v;
@@ -397,6 +445,7 @@ inline void csrAddEdgeU(vector<K>& degrees, vector<K>& edgeKeys, vector<E>& edge
397445
#ifdef OPENMP
398446
/**
399447
* Add a weighted edge to the graph.
448+
* @tparam CHECK check if edge already exists?
400449
* @param degrees degree of each vertex
401450
* @param edgeKeys vertex ids of the outgoing edges of each vertex
402451
* @param edgeValues edge values of the outgoing edges of each vertex
@@ -406,12 +455,14 @@ inline void csrAddEdgeU(vector<K>& degrees, vector<K>& edgeKeys, vector<E>& edge
406455
* @param w associated weight of the edge
407456
* @note Does not check if the edge already exists, or is there is available space.
408457
*/
409-
template <class O, class K, class E>
458+
template <bool CHECK=false, class O, class K, class E>
410459
inline void csrAddEdgeOmpU(vector<K>& degrees, vector<K>& edgeKeys, vector<E>& edgeValues, const vector<O>& offsets, K u, K v, E w) {
460+
if (CHECK && csrHasEdge(offsets, degrees, edgeKeys, u, v)) return;
411461
O n = 0;
412462
#pragma omp atomic capture
413463
{ n = degrees[u]; ++degrees[u]; }
414464
O i = offsets[u] + n;
465+
if (CHECK && offsets[u+1]<=i) return; // Check if there is available space.
415466
edgeKeys[i] = v;
416467
edgeValues[i] = w;
417468
}

inc/leidenLowmem.hxx

+6-1
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,14 @@ inline void leidenLowmemAggregateEdgesOmpW(vector<K>& ydeg, vector<K>& yedg, vec
383383
K d = (*mcs[t])[i];
384384
V w = (*mws[t])[i];
385385
if (!w) continue;
386-
csrAddEdgeU(ydeg, yedg, ywei, yoff, c, d, W(w));
386+
csrAddEdgeOmpU<true>(ydeg, yedg, ywei, yoff, c, d, W(w));
387+
csrAddEdgeOmpU<true>(ydeg, yedg, ywei, yoff, d, c, W(w));
387388
}
388389
}
390+
// Ensure `ydeg` does not exceed bounds.
391+
#pragma omp parallel for schedule(auto)
392+
for (K c=0; c<C; ++c)
393+
ydeg[c] = min(ydeg[c], K(yoff[c+1] - yoff[c]));
389394
}
390395

391396

main.cxx

+4-2
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ void runExperiment(const G& x) {
5252
// Follow a specific result logging format, which can be easily parsed later.
5353
auto flog = [&](const auto& ans, const char *technique, size_t numSlots=0) {
5454
printf(
55-
"{%09.1fms, %09.1fms mark, %09.1fms init, %09.1fms firstpass, %09.1fms locmove, %09.1fms refine, %09.1fms aggr, %.3e slots, %04d iters, %03d passes, %01.9f modularity} %s\n",
55+
"{%09.1fms, %09.1fms mark, %09.1fms init, %09.1fms firstpass, %09.1fms locmove, %09.1fms refine, %09.1fms aggr, %.3e slots, %04d iters, %03d passes, %01.9f modularity, %zu/%zu disconnected} %s\n",
5656
ans.time, ans.markingTime, ans.initializationTime, ans.firstPassTime, ans.localMoveTime, ans.refinementTime, ans.aggregationTime,
57-
double(numSlots), ans.iterations, ans.passes, getModularity(x, ans, M), technique
57+
double(numSlots), ans.iterations, ans.passes, getModularity(x, ans, M),
58+
countValue(communitiesDisconnectedOmp(x, ans.membership), char(1)),
59+
communities(x, ans.membership).size(), technique
5860
);
5961
};
6062
// Get community memberships on original graph (static).

0 commit comments

Comments
 (0)