Skip to content

Commit

Permalink
Merge 9d55190 into 86bc785
Browse files Browse the repository at this point in the history
  • Loading branch information
angriman committed Jul 10, 2019
2 parents 86bc785 + 9d55190 commit 77f16bd
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 3 deletions.
83 changes: 82 additions & 1 deletion include/networkit/centrality/ApproxGroupBetweenness.hpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
/*
* ApproxGroupBetweenness.h
* ApproxGroupBetweenness.hpp
*
* Created on: 13.03.2018
* Author: Marvin Pogoda
*/
#ifndef APPROXGROUPBETWEENNESS_H_
#define APPROXGROUPBETWEENNESS_H_

#include <omp.h>

#include <networkit/base/Algorithm.hpp>
#include <networkit/distance/BFS.hpp>
#include <networkit/graph/Graph.hpp>

namespace NetworKit {
Expand Down Expand Up @@ -35,6 +38,12 @@ class ApproxGroupBetweenness : public Algorithm {
*/
std::vector<node> groupMaxBetweenness();

/**
* Returns the score of the given set.
*/
double scoreOfGroup(const std::vector<node> &S,
const bool normalized = false) const;

protected:
const Graph &G;
count n;
Expand All @@ -53,6 +62,78 @@ inline std::vector<node> ApproxGroupBetweenness::groupMaxBetweenness() {
return maxGroup;
}

inline double
ApproxGroupBetweenness::scoreOfGroup(const std::vector<node> &S,
const bool normalized) const {
if (S.empty())
throw std::runtime_error("Error: input group is empty");

count z = G.upperNodeIdBound();
std::vector<bool> inGroup(z);
for (node u : S) {
if (!G.hasNode(u))
throw std::runtime_error("Error, input group contains nodes not in the graph");
if (inGroup[u])
WARN("Input group contains duplicate nodes!");
inGroup[u] = true;
}

std::vector<double> threadScore(omp_get_max_threads());
std::vector<std::vector<double>> depS(omp_get_max_threads(), std::vector<double>(n));
std::vector<std::vector<double>> depV(omp_get_max_threads(), std::vector<double>(n));
std::vector<BFS> bfss(omp_get_max_threads(), BFS(G, 0, true, true));

omp_set_num_threads(1);
auto computeDeps = [&](node source) {

auto &dS = depS[omp_get_thread_num()];
auto &dV = depV[omp_get_thread_num()];
std::fill(dS.begin(), dS.end(), 0);
std::fill(dV.begin(), dV.end(), 0);

BFS &bfs = bfss[omp_get_thread_num()];
bfs.setSource(source);
bfs.run();

double weight;
auto sortedNodes = bfs.getNodesSortedByDistance();
for (auto it = sortedNodes.rbegin(); it != sortedNodes.rend(); ++it) {
node target = *it;
for (node pred : bfs.getPredecessors(target)) {
(bfs.numberOfPaths(pred) / bfs.numberOfPaths(target)).ToDouble(weight);
if (inGroup[pred]) {
dS[pred] += weight * (1 + dV[target]);
if (pred != source) {
threadScore[omp_get_thread_num()] += dS[pred];
}
dS[pred] = 0;
}
else {
dS[pred] += dS[target];
}
dV[pred] += weight * (1 + dV[target]);
}
}
};

G.balancedParallelForNodes(computeDeps);

double result = 0;
for (double curScore : threadScore)
result += curScore;

if (normalized) {
double nPairs = (z - S.size()) * (z - S.size() - 1);
if (nPairs <= 0)
return 0;
if (!G.isDirected())
nPairs /= 2;
result /= nPairs;
}

return result;
}

} /* namespace NetworKit */

#endif /* APPROXGROUPBETWEENNESS_H_ */
106 changes: 104 additions & 2 deletions networkit/cpp/centrality/test/CentralityGTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1457,11 +1457,91 @@ TEST_F(CentralityGTest, testGroupDegreeDirected) {
gdIncludeGroup.scoreOfGroup(gdIncludeGroup.groupMaxDegree()));
}

TEST_F(CentralityGTest, testGroupBetweennessScore) {
/** 0 5
* | \ / |
* 1 - 3 - 4 - 6
* | / \ |
* 2 7
*/
Graph G(8, false, false);
G.addEdge(0, 1);
G.addEdge(1, 2);
G.addEdge(0, 3);
G.addEdge(1, 3);
G.addEdge(2, 3);

G.addEdge(3, 4);

G.addEdge(4, 5);
G.addEdge(4, 6);
G.addEdge(4, 7);
G.addEdge(5, 6);
G.addEdge(6, 7);

// Naively computes the group betweenness of a group of nodes S
auto naiveGB = [&](const std::vector<node> &S) {
double score = 0;
std::vector<bool> inGroup(S.size());
for (node u : S)
inGroup[u] = true;
G.forNodes([&](node source) {
BFS bfs(G, source, true, false);
bfs.run();
G.forNodes([&](node target) {
if (target == source)
return;

auto paths = bfs.getPaths(target);
if (paths.empty())
return;
double curScore = 0;
for (auto &path : paths) {
for (node u : path) {
if (u != source && u != target && inGroup[u])
curScore += 1;
}
}

score += curScore / static_cast<double>(paths.size());
});
});

return score;
};

ApproxGroupBetweenness gb(G, 2, 0.1);
Betweenness bc(G);
bc.run();
EXPECT_EQ(gb.scoreOfGroup({3}), bc.scores()[3]);
EXPECT_EQ(gb.scoreOfGroup({3}), gb.scoreOfGroup({4}));
EXPECT_EQ(gb.scoreOfGroup({3}, true), gb.scoreOfGroup({4}, true));
count z = G.numberOfNodes();
for (count k = 1; k <= z; ++k) {
std::vector<bool> inGroup(z);
for (index i = 0; i < k; ++i)
inGroup[z - i - 1] = true;
do {
std::vector<node> group;
group.reserve(k);
for (node u = 0; u < z; ++u) {
if (inGroup[u]) {
group.push_back(u);
if (group.size() == k)
break;
}
}
EXPECT_EQ(gb.scoreOfGroup(group), naiveGB(group));
} while (std::next_permutation(inGroup.begin(), inGroup.end()));
}
}

TEST_F(CentralityGTest, runTestApproxGroupBetweennessSmallGraph) {

Aux::Random::setSeed(42, false);

Graph g(8, false, false);
count n = 8;
Graph g(n, false, false);

g.addEdge(0, 2);
g.addEdge(1, 2);
Expand All @@ -1473,8 +1553,30 @@ TEST_F(CentralityGTest, runTestApproxGroupBetweennessSmallGraph) {
g.addEdge(5, 7);
g.addEdge(0, 5);

ApproxGroupBetweenness gb(g, 2, 0.1);
count k = 2;
double eps = 0.1;
ApproxGroupBetweenness gb(g, k, eps);
gb.run();

double maxScore = 0;
std::vector<bool> inGroup(n);
for (index i = 0; i < k; ++i)
inGroup[n - i - 1] = true;
do {
std::vector<node> group;
group.reserve(k);
for (node u = 0; u < n; ++u) {
if (inGroup[u]) {
group.push_back(u);
if (group.size() == k)
break;
}
}

maxScore = std::max(maxScore, gb.scoreOfGroup(group));
} while (std::next_permutation(inGroup.begin(), inGroup.end()));

EXPECT_TRUE(gb.scoreOfGroup(gb.groupMaxBetweenness()) >= maxScore * eps);
}

TEST_F(CentralityGTest, testGroupCloseness) {
Expand Down

0 comments on commit 77f16bd

Please sign in to comment.