/* * Copyright 2008 Network Engine for Objects in Lund AB [neotechnology.com] * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.graphalgo.centrality; import java.util.Set; import org.neo4j.graphalgo.shortestpath.CostAccumulator; import org.neo4j.graphalgo.shortestpath.SingleSourceShortestPath; import org.neo4j.graphdb.Node; /** * Implementation of closeness centrality, which can be seen as the "average" * distance from every node to all other nodes. * @complexity Using a {@link SingleSourceShortestPath} algorithm with time * complexity A, this algorithm runs in time O(A + n) for every * vertex the closeness is to be computed for. Thus doing it for all * vertices takes O(n * (A + m)) time. * @author Patrik Larsson * @param <ShortestPathCostType> * The datatype used by the underlying * {@link SingleSourceShortestPath} algorithm, i.e. the type the edge * weights are represented by. */ public class ClosenessCentrality<ShortestPathCostType> extends ShortestPathBasedCentrality<ShortestPathCostType,ShortestPathCostType> { CostDivider<ShortestPathCostType> centralityDivider; /** * Default constructor. * @param singleSourceShortestPath * Underlying singleSourceShortestPath. * @param centralityAccumulator * Object capable of adding distances. Needed since an "average" * will be computed. * @param zeroValue * Default value and starting value to the accumulator. * @param nodeSet * A set containing the nodes for which centrality values should * be computed. * @param centralityDivider * An object capable of inverting a distance. */ public ClosenessCentrality( SingleSourceShortestPath<ShortestPathCostType> singleSourceShortestPath, CostAccumulator<ShortestPathCostType> centralityAccumulator, ShortestPathCostType zeroValue, Set<Node> nodeSet, CostDivider<ShortestPathCostType> centralityDivider ) { super( singleSourceShortestPath, centralityAccumulator, zeroValue, nodeSet ); this.centralityDivider = centralityDivider; } /* * Since we dont need to do the calculation for all the nodes before we get * a usable result, we can just calculate the result for any given node when * it is asked for. This function just checks if the value has been computed * before, and computes it if needed. */ @Override public ShortestPathCostType getCentrality( Node node ) { ShortestPathCostType centrality = centralities.get( node ); if ( centrality == null ) { return null; } // Not calculated yet, or if it actually is 0 it is very fast to // compute so just do it. if ( centrality.equals( zeroValue ) ) { singleSourceShortestPath.reset(); singleSourceShortestPath.setStartNode( node ); processShortestPaths( node, singleSourceShortestPath ); } // When the value is calculated, just retrieve it normally return centralities.get( node ); } @Override public void processShortestPaths( Node node, SingleSourceShortestPath<ShortestPathCostType> singleSourceShortestPath ) { ShortestPathCostType shortestPathSum = null; for ( Node targetNode : nodeSet ) { if ( shortestPathSum == null ) { shortestPathSum = singleSourceShortestPath.getCost( targetNode ); } else { shortestPathSum = centralityAccumulator.addCosts( shortestPathSum, singleSourceShortestPath .getCost( targetNode ) ); } } // TODO: what should the result be when sum is 0 ? if ( !shortestPathSum.equals( zeroValue ) ) { setCentralityForNode( node, centralityDivider.divideByCost( 1.0, shortestPathSum ) ); } } }