--- /dev/null
+/**
+ * Copyright (c) 2008, the JUNG Project and the Regents of the University
+ * of California
+ * All rights reserved.
+ *
+ * This software is open-source under the BSD license; see either
+ * "license.txt" or
+ * http://jung.sourceforge.net/license.txt for a description.
+ * Created on Sep 16, 2008
+ *
+ */
+package edu.uci.ics.jung.algorithms.scoring;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Stack;
+
+import org.apache.commons.collections15.Transformer;
+import org.apache.commons.collections15.functors.ConstantTransformer;
+
+import edu.uci.ics.jung.algorithms.util.MapBinaryHeap;
+import edu.uci.ics.jung.graph.Graph;
+import edu.uci.ics.jung.graph.UndirectedGraph;
+
+/**
+ * Computes betweenness centrality for each vertex and edge in the graph.
+ *
+ * @see "Ulrik Brandes: A Faster Algorithm for Betweenness Centrality. Journal of Mathematical Sociology 25(2):163-177, 2001."
+ */
+public class BetweennessCentrality<V, E>
+ implements VertexScorer<V, Double>, EdgeScorer<E, Double>
+{
+ protected Graph<V,E> graph;
+ protected Map<V, Double> vertex_scores;
+ protected Map<E, Double> edge_scores;
+ protected Map<V, BetweennessData> vertex_data;
+
+ /**
+ * Calculates betweenness scores based on the all-pairs unweighted shortest paths
+ * in the graph.
+ * @param graph the graph for which the scores are to be calculated
+ */
+ @SuppressWarnings("unchecked")
+ public BetweennessCentrality(Graph<V, E> graph)
+ {
+ initialize(graph);
+ computeBetweenness(new LinkedList<V>(), new ConstantTransformer(1));
+ }
+
+ /**
+ * Calculates betweenness scores based on the all-pairs weighted shortest paths in the
+ * graph.
+ *
+ * <p>NOTE: This version of the algorithm may not work correctly on all graphs; we're still
+ * working out the bugs. Use at your own risk.
+ * @param graph the graph for which the scores are to be calculated
+ * @param edge_weights the edge weights to be used in the path length calculations
+ */
+ public BetweennessCentrality(Graph<V, E> graph,
+ Transformer<E, ? extends Number> edge_weights)
+ {
+ // reject negative-weight edges up front
+ for (E e : graph.getEdges())
+ {
+ double e_weight = edge_weights.transform(e).doubleValue();
+ if (e_weight < 0)
+ throw new IllegalArgumentException(String.format(
+ "Weight for edge '%s' is < 0: %d", e, e_weight));
+ }
+
+ initialize(graph);
+ computeBetweenness(new MapBinaryHeap<V>(new BetweennessComparator()),
+ edge_weights);
+ }
+
+ protected void initialize(Graph<V,E> graph)
+ {
+ this.graph = graph;
+ this.vertex_scores = new HashMap<V, Double>();
+ this.edge_scores = new HashMap<E, Double>();
+ this.vertex_data = new HashMap<V, BetweennessData>();
+
+ for (V v : graph.getVertices())
+ this.vertex_scores.put(v, 0.0);
+
+ for (E e : graph.getEdges())
+ this.edge_scores.put(e, 0.0);
+ }
+
+ protected void computeBetweenness(Queue<V> queue,
+ Transformer<E, ? extends Number> edge_weights)
+ {
+ for (V v : graph.getVertices())
+ {
+ // initialize the betweenness data for this new vertex
+ for (V s : graph.getVertices())
+ this.vertex_data.put(s, new BetweennessData());
+
+// if (v.equals(new Integer(0)))
+// System.out.println("pause");
+
+ vertex_data.get(v).numSPs = 1;
+ vertex_data.get(v).distance = 0;
+
+ Stack<V> stack = new Stack<V>();
+// Buffer<V> queue = new UnboundedFifoBuffer<V>();
+// queue.add(v);
+ queue.offer(v);
+
+ while (!queue.isEmpty())
+ {
+// V w = queue.remove();
+ V w = queue.poll();
+ stack.push(w);
+ BetweennessData w_data = vertex_data.get(w);
+
+ for (E e : graph.getOutEdges(w))
+ {
+ // TODO (jrtom): change this to getOtherVertices(w, e)
+ V x = graph.getOpposite(w, e);
+ if (x.equals(w))
+ continue;
+ double wx_weight = edge_weights.transform(e).doubleValue();
+
+
+// for(V x : graph.getSuccessors(w))
+// {
+// if (x.equals(w))
+// continue;
+
+ // FIXME: the other problem is that I need to
+ // keep putting the neighbors of things we've just
+ // discovered in the queue, if they're undiscovered or
+ // at greater distance.
+
+ // FIXME: this is the problem, right here, I think:
+ // need to update position in queue if distance changes
+ // (which can only happen with weighted edges).
+ // for each outgoing edge e from w, get other end x
+ // if x not already visited (dist x < 0)
+ // set x's distance to w's dist + edge weight
+ // add x to queue; pri in queue is x's dist
+ // if w's dist + edge weight < x's dist
+ // update x's dist
+ // update x in queue (MapBinaryHeap)
+ // clear x's incoming edge list
+ // if w's dist + edge weight = x's dist
+ // add e to x's incoming edge list
+
+ BetweennessData x_data = vertex_data.get(x);
+ double x_potential_dist = w_data.distance + wx_weight;
+
+ if (x_data.distance < 0)
+ {
+// queue.add(x);
+// vertex_data.get(x).distance = vertex_data.get(w).distance + 1;
+ x_data.distance = x_potential_dist;
+ queue.offer(x);
+ }
+
+ // note:
+ // (1) this can only happen with weighted edges
+ // (2) x's SP count and incoming edges are updated below
+ if (x_data.distance > x_potential_dist)
+ {
+ x_data.distance = x_potential_dist;
+ // invalidate previously identified incoming edges
+ // (we have a new shortest path distance to x)
+ x_data.incomingEdges.clear();
+ // update x's position in queue
+ ((MapBinaryHeap<V>)queue).update(x);
+ }
+// if (vertex_data.get(x).distance == vertex_data.get(w).distance + 1)
+ //
+// if (x_data.distance == x_potential_dist)
+// {
+// x_data.numSPs += w_data.numSPs;
+//// vertex_data.get(x).predecessors.add(w);
+// x_data.incomingEdges.add(e);
+// }
+ }
+ for (E e: graph.getOutEdges(w))
+ {
+ V x = graph.getOpposite(w, e);
+ if (x.equals(w))
+ continue;
+ double e_weight = edge_weights.transform(e).doubleValue();
+ BetweennessData x_data = vertex_data.get(x);
+ double x_potential_dist = w_data.distance + e_weight;
+ if (x_data.distance == x_potential_dist)
+ {
+ x_data.numSPs += w_data.numSPs;
+// vertex_data.get(x).predecessors.add(w);
+ x_data.incomingEdges.add(e);
+ }
+ }
+ }
+ while (!stack.isEmpty())
+ {
+ V x = stack.pop();
+
+// for (V w : vertex_data.get(x).predecessors)
+ for (E e : vertex_data.get(x).incomingEdges)
+ {
+ V w = graph.getOpposite(x, e);
+ double partialDependency =
+ vertex_data.get(w).numSPs / vertex_data.get(x).numSPs *
+ (1.0 + vertex_data.get(x).dependency);
+ vertex_data.get(w).dependency += partialDependency;
+// E w_x = graph.findEdge(w, x);
+// double w_x_score = edge_scores.get(w_x).doubleValue();
+// w_x_score += partialDependency;
+// edge_scores.put(w_x, w_x_score);
+ double e_score = edge_scores.get(e).doubleValue();
+ edge_scores.put(e, e_score + partialDependency);
+ }
+ if (!x.equals(v))
+ {
+ double x_score = vertex_scores.get(x).doubleValue();
+ x_score += vertex_data.get(x).dependency;
+ vertex_scores.put(x, x_score);
+ }
+ }
+ }
+
+ if(graph instanceof UndirectedGraph)
+ {
+ for (V v : graph.getVertices()) {
+ double v_score = vertex_scores.get(v).doubleValue();
+ v_score /= 2.0;
+ vertex_scores.put(v, v_score);
+ }
+ for (E e : graph.getEdges()) {
+ double e_score = edge_scores.get(e).doubleValue();
+ e_score /= 2.0;
+ edge_scores.put(e, e_score);
+ }
+ }
+
+ vertex_data.clear();
+ }
+
+// protected void computeWeightedBetweenness(Transformer<E, ? extends Number> edge_weights)
+// {
+// for (V v : graph.getVertices())
+// {
+// // initialize the betweenness data for this new vertex
+// for (V s : graph.getVertices())
+// this.vertex_data.put(s, new BetweennessData());
+// vertex_data.get(v).numSPs = 1;
+// vertex_data.get(v).distance = 0;
+//
+// Stack<V> stack = new Stack<V>();
+//// Buffer<V> queue = new UnboundedFifoBuffer<V>();
+// SortedSet<V> pqueue = new TreeSet<V>(new BetweennessComparator());
+//// queue.add(v);
+// pqueue.add(v);
+//
+//// while (!queue.isEmpty())
+// while (!pqueue.isEmpty())
+// {
+//// V w = queue.remove();
+// V w = pqueue.first();
+// pqueue.remove(w);
+// stack.push(w);
+//
+//// for(V x : graph.getSuccessors(w))
+// for (E e : graph.getOutEdges(w))
+// {
+// // TODO (jrtom): change this to getOtherVertices(w, e)
+// V x = graph.getOpposite(w, e);
+// if (x.equals(w))
+// continue;
+// double e_weight = edge_weights.transform(e).doubleValue();
+//
+// if (vertex_data.get(x).distance < 0)
+// {
+//// queue.add(x);
+// pqueue.add(v);
+//// vertex_data.get(x).distance = vertex_data.get(w).distance + 1;
+// vertex_data.get(x).distance =
+// vertex_data.get(w).distance + e_weight;
+// }
+//
+//// if (vertex_data.get(x).distance == vertex_data.get(w).distance + 1)
+// if (vertex_data.get(x).distance ==
+// vertex_data.get(w).distance + e_weight)
+// {
+// vertex_data.get(x).numSPs += vertex_data.get(w).numSPs;
+// vertex_data.get(x).predecessors.add(w);
+// }
+// }
+// }
+// updateScores(v, stack);
+// }
+//
+// if(graph instanceof UndirectedGraph)
+// adjustUndirectedScores();
+//
+// vertex_data.clear();
+// }
+
+ public Double getVertexScore(V v)
+ {
+ return vertex_scores.get(v);
+ }
+
+ public Double getEdgeScore(E e)
+ {
+ return edge_scores.get(e);
+ }
+
+ private class BetweennessData
+ {
+ double distance;
+ double numSPs;
+// List<V> predecessors;
+ List<E> incomingEdges;
+ double dependency;
+
+ BetweennessData()
+ {
+ distance = -1;
+ numSPs = 0;
+// predecessors = new ArrayList<V>();
+ incomingEdges = new ArrayList<E>();
+ dependency = 0;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "[d:" + distance + ", sp:" + numSPs +
+ ", p:" + incomingEdges + ", d:" + dependency + "]\n";
+// ", p:" + predecessors + ", d:" + dependency + "]\n";
+ }
+ }
+
+ private class BetweennessComparator implements Comparator<V>
+ {
+ public int compare(V v1, V v2)
+ {
+ return vertex_data.get(v1).distance > vertex_data.get(v2).distance ? 1 : -1;
+ }
+ }
+}