edu.uci.ics.jung.algorithms.scoring.BetweennessCentrality.java Source code

Java tutorial

Introduction

Here is the source code for edu.uci.ics.jung.algorithms.scoring.BetweennessCentrality.java

Source

/**
 * 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;
        }
    }
}