nxpp
Header-only graph utilities on top of Boost Graph Library
Loading...
Searching...
No Matches
centrality.hpp
Go to the documentation of this file.
1#pragma once
2
11#include "graph.hpp"
12
13namespace nxpp {
14
15template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
17 const auto node_count = boost::num_vertices(g);
18 std::vector<double> centrality_by_index(static_cast<std::size_t>(node_count), 0.0);
19 if (node_count == 0) {
21 }
22 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
23 centrality_by_index[get_vertex_index(*v)] = 0.0;
24 }
25 if (node_count <= 1) {
26 return build_node_indexed_result<double>([&](VertexDesc v) {
27 return centrality_by_index[get_vertex_index(v)];
28 });
29 }
30 const double scale = 1.0 / static_cast<double>(node_count - 1);
31 for (auto [v, vend] = boost::vertices(g); v != vend; ++v) {
32 double degree = 0.0;
33 if constexpr (Directed) {
34 degree = static_cast<double>(boost::in_degree(*v, g) + boost::out_degree(*v, g));
35 } else {
36 degree = static_cast<double>(boost::degree(*v, g));
37 }
38 centrality_by_index[get_vertex_index(*v)] = degree * scale;
39 }
40 return build_node_indexed_result<double>([&](VertexDesc v) {
41 return centrality_by_index[get_vertex_index(v)];
42 });
43}
44
45template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
47 const auto node_count = boost::num_vertices(g);
48 if (node_count == 0) {
50 }
51
52 const double n = static_cast<double>(node_count);
53 const double damping = 0.85;
54 std::vector<double> rank(static_cast<std::size_t>(node_count), 1.0 / n);
55 std::vector<double> next(static_cast<std::size_t>(node_count), 0.0);
56
57 for (int iteration = 0; iteration < 20; ++iteration) {
58 const double base = (1.0 - damping) / n;
59 std::fill(next.begin(), next.end(), base);
60
61 double dangling_sum = 0.0;
62 for (auto [u, uend] = boost::vertices(g); u != uend; ++u) {
63 const auto out = boost::out_degree(*u, g);
64 const auto index = get_vertex_index(*u);
65 if (out == 0) {
66 dangling_sum += rank[index];
67 continue;
68 }
69
70 const double share = damping * rank[index] / static_cast<double>(out);
71 for (auto [adj, adj_end] = boost::adjacent_vertices(*u, g); adj != adj_end; ++adj) {
72 next[get_vertex_index(*adj)] += share;
73 }
74 }
75
76 if (dangling_sum > 0.0) {
77 const double dangling_share = damping * dangling_sum / n;
78 for (double& value : next) {
79 value += dangling_share;
80 }
81 }
82
83 rank.swap(next);
84 }
85
86 return build_node_indexed_result<double>([&](VertexDesc v) {
87 return rank[get_vertex_index(v)];
88 });
89}
90
91template <typename GraphWrapper>
99[[deprecated("Use G.pagerank() instead.")]]
100auto pagerank(const GraphWrapper& G) {
101 return G.pagerank();
102}
103
104template <typename GraphWrapper>
112[[deprecated("Use G.degree_centrality() instead.")]]
113auto degree_centrality(const GraphWrapper& G) {
114 return G.degree_centrality();
115}
116
117template <typename NodeID, typename EdgeWeight, bool Directed, bool Multi, bool Weighted, typename OutEdgeSelector, typename VertexSelector>
119 const auto node_count = boost::num_vertices(g);
120 if (node_count == 0) {
122 }
123
124 const std::size_t n = static_cast<std::size_t>(node_count);
125 std::vector<double> bc(n, 0.0);
126
127 // Brandes algorithm: for each source, run BFS to count shortest paths,
128 // then back-propagate dependency scores to accumulate betweenness.
129 for (auto [s_it, s_end] = boost::vertices(g); s_it != s_end; ++s_it) {
130 const VertexDesc s = *s_it;
131 const std::size_t s_idx = get_vertex_index(s);
132
133 // Vertices in order of non-increasing distance from s (for back-propagation).
134 std::vector<std::size_t> stack;
135 stack.reserve(n);
136
137 // Predecessors on shortest paths from s, indexed by vertex index.
138 std::vector<std::vector<std::size_t>> pred(n);
139
140 // Number of shortest paths from s to each vertex.
141 std::vector<double> sigma(n, 0.0);
142 sigma[s_idx] = 1.0;
143
144 // BFS distance from s (-1 means unvisited).
145 std::vector<int> dist(n, -1);
146 dist[s_idx] = 0;
147
148 std::queue<VertexDesc> bfs_queue;
149 bfs_queue.push(s);
150
151 while (!bfs_queue.empty()) {
152 const VertexDesc v = bfs_queue.front();
153 bfs_queue.pop();
154 const std::size_t v_idx = get_vertex_index(v);
155 stack.push_back(v_idx);
156
157 for (auto [w_it, w_end] = boost::adjacent_vertices(v, g); w_it != w_end; ++w_it) {
158 const std::size_t w_idx = get_vertex_index(*w_it);
159
160 if (dist[w_idx] < 0) {
161 bfs_queue.push(*w_it);
162 dist[w_idx] = dist[v_idx] + 1;
163 }
164 if (dist[w_idx] == dist[v_idx] + 1) {
165 sigma[w_idx] += sigma[v_idx];
166 pred[w_idx].push_back(v_idx);
167 }
168 }
169 }
170
171 // Back-propagate dependency in reverse BFS order.
172 std::vector<double> delta(n, 0.0);
173 while (!stack.empty()) {
174 const std::size_t w_idx = stack.back();
175 stack.pop_back();
176 for (const std::size_t v_idx : pred[w_idx]) {
177 if (sigma[w_idx] > 0.0) {
178 delta[v_idx] += (sigma[v_idx] / sigma[w_idx]) * (1.0 + delta[w_idx]);
179 }
180 }
181 if (w_idx != s_idx) {
182 bc[w_idx] += delta[w_idx];
183 }
184 }
185 }
186
187 // Normalize by the number of ordered (directed) or unordered (undirected) pairs,
188 // matching NetworkX betweenness_centrality(G, normalized=True) semantics.
189 if (node_count > 2) {
190 const double denom = static_cast<double>(node_count - 1) * static_cast<double>(node_count - 2);
191 const double scale = Directed ? 1.0 / denom : 2.0 / denom;
192 for (double& v : bc) v *= scale;
193 }
194
195 return build_node_indexed_result<double>([&](VertexDesc v) {
196 return bc[get_vertex_index(v)];
197 });
198}
199
200template <typename GraphWrapper>
208[[deprecated("Use G.betweenness_centrality() instead.")]]
209auto betweenness_centrality(const GraphWrapper& G) {
210 return G.betweenness_centrality();
211}
212
213} // namespace nxpp
auto degree_centrality(const GraphWrapper &G)
Deprecated free-function alias for G.degree_centrality().
Definition centrality.hpp:113
auto betweenness_centrality(const GraphWrapper &G)
Deprecated free-function alias for G.betweenness_centrality().
Definition centrality.hpp:209
auto pagerank(const GraphWrapper &G)
Deprecated free-function alias for G.pagerank().
Definition centrality.hpp:100
auto betweenness_centrality() const
Computes normalized betweenness centrality for every node.
Definition centrality.hpp:118
auto pagerank() const
Computes PageRank scores for every node.
Definition centrality.hpp:46
auto degree_centrality() const
Computes normalized degree centrality for every node.
Definition centrality.hpp:16
Definition graph.hpp:143
Core graph wrapper, proxy surface, and public alias presets.