com.googlecode.blaisemath.graph.mod.metrics.BetweenCentrality.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.blaisemath.graph.mod.metrics.BetweenCentrality.java

Source

/*
 * BetweenCentrality.java
 * Created Jul 3, 2010
 */
package com.googlecode.blaisemath.graph.mod.metrics;

/*
 * #%L
 * BlaiseGraphTheory
 * --
 * Copyright (C) 2009 - 2016 Elisha Peterson
 * --
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import com.google.common.collect.HashMultimap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Queues;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.googlecode.blaisemath.util.GAInstrument;
import com.googlecode.blaisemath.graph.Graph;
import com.googlecode.blaisemath.graph.GraphUtils;
import java.util.Deque;

/**
 * <p> Provides a metric describing the betweenness centrality of a vertex in a
 * CONNECTED graph. Returns infinity if the graph is not connected. May take a
 * long time for large graphs. </p> <p> Computationally, the centrality measures
 * the probability that a given node lies on a randomly chosen geodesic. </p>
 *
 * @author Elisha Peterson
 */
public class BetweenCentrality extends AbstractGraphNodeMetric<Double> {

    public BetweenCentrality() {
        super("Betweenness centrality");
    }

    @Override
    public <V> Double apply(Graph<V> graph, V node) {
        return apply(graph).get(node);
    }

    @Override
    public <V> Map<V, Double> apply(Graph<V> graph) {
        int id = GAInstrument.start("BetweenCentrality.allValues", graph.nodeCount() + " nodes",
                graph.edgeCount() + " edges");
        Map<V, Double> between = new HashMap<V, Double>();
        for (V v : graph.nodes()) {
            between.put(v, 0.0);
        }
        for (V start : graph.nodes()) {
            brandes(graph, start, between, graph.isDirected() ? 1.0 : 0.5);
        }
        GAInstrument.end(id);
        return between;
    }

    /**
     * Breadth-first search algorithm for an unweighted graph to generate
     * betweenness scores, with specified starting vertex. From <i>Brandes</i>,
     * "A Faster Algorithm for Betweenness Centrality"
     *
     * @param graph the graph
     * @param start the start vertex
     * @param between data structure storing existing betweenness centrality values
     * @param multiplier applied to all elements of resulting map
     * @return data structure encoding the result
     */
    static <V> Map<V, Double> brandes(Graph<V> graph, V start, Map<V, Double> between, double multiplier) {
        Set<V> nodes = graph.nodes();
        if (!nodes.contains(start)) {
            return new HashMap<V, Double>();
        }

        // number of shortest paths to each vertex
        Multiset<V> numShortest = HashMultiset.create();
        // length of shortest paths to each vertex
        Map<V, Integer> lengths = new HashMap<V, Integer>();
        // tracks elements in non-increasing order for later use
        Deque<V> deque = Queues.newArrayDeque();
        // tracks vertex predecessors in resulting tree
        Multimap<V, V> pred = HashMultimap.create();

        GraphUtils.breadthFirstSearch(graph, start, numShortest, lengths, deque, pred);

        // compute betweenness
        Map<V, Double> dependencies = new HashMap<V, Double>();
        for (V v : nodes) {
            dependencies.put(v, 0.0);
        }
        while (!deque.isEmpty()) {
            V w = deque.pollLast();
            for (V v : pred.get(w)) {
                dependencies.put(v, dependencies.get(v)
                        + (double) numShortest.count(v) / numShortest.count(w) * (1 + dependencies.get(w)));
            }
            if (w != start) {
                between.put(w, between.get(w) + multiplier * dependencies.get(w));
            }
        }

        return between;

    }

}