1 /**
2 * Copyright (c) 2008, The JUNG Authors
3 *
4 * All rights reserved.
5 *
6 * This software is open-source under the BSD license; see either
7 * "license.txt" or
8 * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
9 * Created on Sep 16, 2008
10 *
11 */
12 package edu.uci.ics.jung.algorithms.scoring;
13
14 import java.util.ArrayList;
15 import java.util.Comparator;
16 import java.util.HashMap;
17 import java.util.LinkedList;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Queue;
21 import java.util.Stack;
22
23 import com.google.common.base.Function;
24 import com.google.common.base.Functions;
25
26 import edu.uci.ics.jung.algorithms.util.MapBinaryHeap;
27 import edu.uci.ics.jung.graph.Graph;
28 import edu.uci.ics.jung.graph.UndirectedGraph;
29
30 /**
31 * Computes betweenness centrality for each vertex and edge in the graph.
32 *
33 * @see "Ulrik Brandes: A Faster Algorithm for Betweenness Centrality. Journal of Mathematical Sociology 25(2):163-177, 2001."
34 */
35 public class BetweennessCentrality<V, E>
36 implements VertexScorer<V, Double>, EdgeScorer<E, Double>
37 {
38 protected Graph<V,E> graph;
39 protected Map<V, Double> vertex_scores;
40 protected Map<E, Double> edge_scores;
41 protected Map<V, BetweennessData> vertex_data;
42
43 /**
44 * Calculates betweenness scores based on the all-pairs unweighted shortest paths
45 * in the graph.
46 * @param graph the graph for which the scores are to be calculated
47 */
48 public BetweennessCentrality(Graph<V, E> graph)
49 {
50 initialize(graph);
51 computeBetweenness(new LinkedList<V>(), Functions.<Integer>constant(1));
52 }
53
54 /**
55 * Calculates betweenness scores based on the all-pairs weighted shortest paths in the
56 * graph.
57 *
58 * <p>NOTE: This version of the algorithm may not work correctly on all graphs; we're still
59 * working out the bugs. Use at your own risk.
60 * @param graph the graph for which the scores are to be calculated
61 * @param edge_weights the edge weights to be used in the path length calculations
62 */
63 public BetweennessCentrality(Graph<V, E> graph,
64 Function<? super E, ? extends Number> edge_weights)
65 {
66 // reject negative-weight edges up front
67 for (E e : graph.getEdges())
68 {
69 double e_weight = edge_weights.apply(e).doubleValue();
70 if (e_weight < 0)
71 throw new IllegalArgumentException(String.format(
72 "Weight for edge '%s' is < 0: %d", e, e_weight));
73 }
74
75 initialize(graph);
76 computeBetweenness(new MapBinaryHeap<V>(new BetweennessComparator()),
77 edge_weights);
78 }
79
80 protected void initialize(Graph<V,E> graph)
81 {
82 this.graph = graph;
83 this.vertex_scores = new HashMap<V, Double>();
84 this.edge_scores = new HashMap<E, Double>();
85 this.vertex_data = new HashMap<V, BetweennessData>();
86
87 for (V v : graph.getVertices())
88 this.vertex_scores.put(v, 0.0);
89
90 for (E e : graph.getEdges())
91 this.edge_scores.put(e, 0.0);
92 }
93
94 protected void computeBetweenness(Queue<V> queue,
95 Function<? super E, ? extends Number> edge_weights)
96 {
97 for (V v : graph.getVertices())
98 {
99 // initialize the betweenness data for this new vertex
100 for (V s : graph.getVertices())
101 this.vertex_data.put(s, new BetweennessData());
102
103 // if (v.equals(new Integer(0)))
104 // System.out.println("pause");
105
106 vertex_data.get(v).numSPs = 1;
107 vertex_data.get(v).distance = 0;
108
109 Stack<V> stack = new Stack<V>();
110 // Buffer<V> queue = new UnboundedFifoBuffer<V>();
111 // queue.add(v);
112 queue.offer(v);
113
114 while (!queue.isEmpty())
115 {
116 // V w = queue.remove();
117 V w = queue.poll();
118 stack.push(w);
119 BetweennessData w_data = vertex_data.get(w);
120
121 for (E e : graph.getOutEdges(w))
122 {
123 // TODO (jrtom): change this to getOtherVertices(w, e)
124 V x = graph.getOpposite(w, e);
125 if (x.equals(w))
126 continue;
127 double wx_weight = edge_weights.apply(e).doubleValue();
128
129
130 // for(V x : graph.getSuccessors(w))
131 // {
132 // if (x.equals(w))
133 // continue;
134
135 // FIXME: the other problem is that I need to
136 // keep putting the neighbors of things we've just
137 // discovered in the queue, if they're undiscovered or
138 // at greater distance.
139
140 // FIXME: this is the problem, right here, I think:
141 // need to update position in queue if distance changes
142 // (which can only happen with weighted edges).
143 // for each outgoing edge e from w, get other end x
144 // if x not already visited (dist x < 0)
145 // set x's distance to w's dist + edge weight
146 // add x to queue; pri in queue is x's dist
147 // if w's dist + edge weight < x's dist
148 // update x's dist
149 // update x in queue (MapBinaryHeap)
150 // clear x's incoming edge list
151 // if w's dist + edge weight = x's dist
152 // add e to x's incoming edge list
153
154 BetweennessData x_data = vertex_data.get(x);
155 double x_potential_dist = w_data.distance + wx_weight;
156
157 if (x_data.distance < 0)
158 {
159 // queue.add(x);
160 // vertex_data.get(x).distance = vertex_data.get(w).distance + 1;
161 x_data.distance = x_potential_dist;
162 queue.offer(x);
163 }
164
165 // note:
166 // (1) this can only happen with weighted edges
167 // (2) x's SP count and incoming edges are updated below
168 if (x_data.distance > x_potential_dist)
169 {
170 x_data.distance = x_potential_dist;
171 // invalidate previously identified incoming edges
172 // (we have a new shortest path distance to x)
173 x_data.incomingEdges.clear();
174 // update x's position in queue
175 ((MapBinaryHeap<V>)queue).update(x);
176 }
177 // if (vertex_data.get(x).distance == vertex_data.get(w).distance + 1)
178 //
179 // if (x_data.distance == x_potential_dist)
180 // {
181 // x_data.numSPs += w_data.numSPs;
182 //// vertex_data.get(x).predecessors.add(w);
183 // x_data.incomingEdges.add(e);
184 // }
185 }
186 for (E e: graph.getOutEdges(w))
187 {
188 V x = graph.getOpposite(w, e);
189 if (x.equals(w))
190 continue;
191 double e_weight = edge_weights.apply(e).doubleValue();
192 BetweennessData x_data = vertex_data.get(x);
193 double x_potential_dist = w_data.distance + e_weight;
194 if (x_data.distance == x_potential_dist)
195 {
196 x_data.numSPs += w_data.numSPs;
197 // vertex_data.get(x).predecessors.add(w);
198 x_data.incomingEdges.add(e);
199 }
200 }
201 }
202 while (!stack.isEmpty())
203 {
204 V x = stack.pop();
205
206 // for (V w : vertex_data.get(x).predecessors)
207 for (E e : vertex_data.get(x).incomingEdges)
208 {
209 V w = graph.getOpposite(x, e);
210 double partialDependency =
211 vertex_data.get(w).numSPs / vertex_data.get(x).numSPs *
212 (1.0 + vertex_data.get(x).dependency);
213 vertex_data.get(w).dependency += partialDependency;
214 // E w_x = graph.findEdge(w, x);
215 // double w_x_score = edge_scores.get(w_x).doubleValue();
216 // w_x_score += partialDependency;
217 // edge_scores.put(w_x, w_x_score);
218 double e_score = edge_scores.get(e).doubleValue();
219 edge_scores.put(e, e_score + partialDependency);
220 }
221 if (!x.equals(v))
222 {
223 double x_score = vertex_scores.get(x).doubleValue();
224 x_score += vertex_data.get(x).dependency;
225 vertex_scores.put(x, x_score);
226 }
227 }
228 }
229
230 if(graph instanceof UndirectedGraph)
231 {
232 for (V v : graph.getVertices()) {
233 double v_score = vertex_scores.get(v).doubleValue();
234 v_score /= 2.0;
235 vertex_scores.put(v, v_score);
236 }
237 for (E e : graph.getEdges()) {
238 double e_score = edge_scores.get(e).doubleValue();
239 e_score /= 2.0;
240 edge_scores.put(e, e_score);
241 }
242 }
243
244 vertex_data.clear();
245 }
246
247 // protected void computeWeightedBetweenness(Function<E, ? extends Number> edge_weights)
248 // {
249 // for (V v : graph.getVertices())
250 // {
251 // // initialize the betweenness data for this new vertex
252 // for (V s : graph.getVertices())
253 // this.vertex_data.put(s, new BetweennessData());
254 // vertex_data.get(v).numSPs = 1;
255 // vertex_data.get(v).distance = 0;
256 //
257 // Stack<V> stack = new Stack<V>();
258 //// Buffer<V> queue = new UnboundedFifoBuffer<V>();
259 // SortedSet<V> pqueue = new TreeSet<V>(new BetweennessComparator());
260 //// queue.add(v);
261 // pqueue.add(v);
262 //
263 //// while (!queue.isEmpty())
264 // while (!pqueue.isEmpty())
265 // {
266 //// V w = queue.remove();
267 // V w = pqueue.first();
268 // pqueue.remove(w);
269 // stack.push(w);
270 //
271 //// for(V x : graph.getSuccessors(w))
272 // for (E e : graph.getOutEdges(w))
273 // {
274 // // TODO (jrtom): change this to getOtherVertices(w, e)
275 // V x = graph.getOpposite(w, e);
276 // if (x.equals(w))
277 // continue;
278 // double e_weight = edge_weights.transform(e).doubleValue();
279 //
280 // if (vertex_data.get(x).distance < 0)
281 // {
282 //// queue.add(x);
283 // pqueue.add(v);
284 //// vertex_data.get(x).distance = vertex_data.get(w).distance + 1;
285 // vertex_data.get(x).distance =
286 // vertex_data.get(w).distance + e_weight;
287 // }
288 //
289 //// if (vertex_data.get(x).distance == vertex_data.get(w).distance + 1)
290 // if (vertex_data.get(x).distance ==
291 // vertex_data.get(w).distance + e_weight)
292 // {
293 // vertex_data.get(x).numSPs += vertex_data.get(w).numSPs;
294 // vertex_data.get(x).predecessors.add(w);
295 // }
296 // }
297 // }
298 // updateScores(v, stack);
299 // }
300 //
301 // if(graph instanceof UndirectedGraph)
302 // adjustUndirectedScores();
303 //
304 // vertex_data.clear();
305 // }
306
307 public Double getVertexScore(V v)
308 {
309 return vertex_scores.get(v);
310 }
311
312 public Double getEdgeScore(E e)
313 {
314 return edge_scores.get(e);
315 }
316
317 private class BetweennessData
318 {
319 double distance;
320 double numSPs;
321 // List<V> predecessors;
322 List<E> incomingEdges;
323 double dependency;
324
325 BetweennessData()
326 {
327 distance = -1;
328 numSPs = 0;
329 // predecessors = new ArrayList<V>();
330 incomingEdges = new ArrayList<E>();
331 dependency = 0;
332 }
333
334 @Override
335 public String toString()
336 {
337 return "[d:" + distance + ", sp:" + numSPs +
338 ", p:" + incomingEdges + ", d:" + dependency + "]\n";
339 // ", p:" + predecessors + ", d:" + dependency + "]\n";
340 }
341 }
342
343 private class BetweennessComparator implements Comparator<V>
344 {
345 public int compare(V v1, V v2)
346 {
347 return vertex_data.get(v1).distance > vertex_data.get(v2).distance ? 1 : -1;
348 }
349 }
350 }