View Javadoc
1   /*
2   * Copyright (c) 2003, 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   */
10  package edu.uci.ics.jung.algorithms.importance;
11  
12  import java.util.ArrayList;
13  import java.util.Collection;
14  import java.util.HashMap;
15  import java.util.HashSet;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.Set;
19  
20  import com.google.common.base.Supplier;
21  
22  import edu.uci.ics.jung.graph.DirectedGraph;
23  
24  
25  
26  /**
27   * This algorithm measures the importance of nodes based upon both the number and length of disjoint paths that lead
28   * to a given node from each of the nodes in the root set. Specifically the formula for measuring the importance of a
29   * node is given by: I(t|R) = sum_i=1_|P(r,t)|_{alpha^|p_i|} where alpha is the path decay coefficient, p_i is path i
30   * and P(r,t) is a set of maximum-sized node-disjoint paths from r to t.
31   * <p>
32   * This algorithm uses heuristic breadth-first search to try and find the maximum-sized set of node-disjoint paths
33   * between two nodes. As such, it is not guaranteed to give exact answers.
34   * <p>
35   * A simple example of usage is:
36   * <pre>
37   * WeightedNIPaths ranker = new WeightedNIPaths(someGraph,2.0,6,rootSet);
38   * ranker.evaluate();
39   * ranker.printRankings();
40   * </pre>
41   * 
42   * @author Scott White
43   * @see "Algorithms for Estimating Relative Importance in Graphs by Scott White and Padhraic Smyth, 2003"
44   */
45  public class WeightedNIPaths<V,E> extends AbstractRanker<V,E> {
46      public final static String WEIGHTED_NIPATHS_KEY = "jung.algorithms.importance.WEIGHTED_NIPATHS_KEY";
47      private double mAlpha;
48      private int mMaxDepth;
49      private Set<V> mPriors;
50      private Map<E,Number> pathIndices = new HashMap<E,Number>();
51      private Map<Object,V> roots = new HashMap<Object,V>();
52      private Map<V,Set<Number>> pathsSeenMap = new HashMap<V,Set<Number>>();
53      private Supplier<V> vertexFactory;
54      private Supplier<E> edgeFactory;
55  
56      /**
57       * Constructs and initializes the algorithm.
58       * @param graph the graph whose nodes are being measured for their importance
59       * @param vertexFactory used to generate instances of V
60       * @param edgeFactory used to generate instances of E
61       * @param alpha the path decay coefficient (&ge;1); 2 is recommended
62       * @param maxDepth the maximal depth to search out from the root set
63       * @param priors the root set (starting vertices)
64       */
65      public WeightedNIPaths(DirectedGraph<V,E> graph, Supplier<V> vertexFactory,
66      		Supplier<E> edgeFactory, double alpha, int maxDepth, Set<V> priors) {
67          super.initialize(graph, true,false);
68          this.vertexFactory = vertexFactory;
69          this.edgeFactory = edgeFactory;
70          mAlpha = alpha;
71          mMaxDepth = maxDepth;
72          mPriors = priors;
73          for (V v : graph.getVertices()) {
74          	super.setVertexRankScore(v, 0.0);
75          }
76      }
77  
78      protected void incrementRankScore(V v, double rankValue) {
79          setVertexRankScore(v, getVertexRankScore(v) + rankValue);
80      }
81  
82      protected void computeWeightedPathsFromSource(V root, int depth) {
83  
84          int pathIdx = 1;
85  
86          for (E e : getGraph().getOutEdges(root)) {
87              this.pathIndices.put(e, pathIdx);
88              this.roots.put(e, root);
89              newVertexEncountered(pathIdx, getGraph().getEndpoints(e).getSecond(), root);
90              pathIdx++;
91          }
92  
93          List<E> edges = new ArrayList<E>();
94  
95          V virtualNode = vertexFactory.get();
96          getGraph().addVertex(virtualNode);
97          E virtualSinkEdge = edgeFactory.get();
98  
99          getGraph().addEdge(virtualSinkEdge, virtualNode, root);
100         edges.add(virtualSinkEdge);
101 
102         int currentDepth = 0;
103         while (currentDepth <= depth) {
104 
105             double currentWeight = Math.pow(mAlpha, -1.0 * currentDepth);
106             for (E currentEdge : edges) { 
107                 incrementRankScore(getGraph().getEndpoints(currentEdge).getSecond(),//
108                 		currentWeight);
109             }
110 
111             if ((currentDepth == depth) || (edges.size() == 0)) break;
112 
113             List<E> newEdges = new ArrayList<E>();
114 
115             for (E currentSourceEdge : edges) { //Iterator sourceEdgeIt = edges.iterator(); sourceEdgeIt.hasNext();) {
116                 Number sourcePathIndex = this.pathIndices.get(currentSourceEdge);
117 
118                 // from the currentSourceEdge, get its opposite end
119                 // then iterate over the out edges of that opposite end
120                 V newDestVertex = getGraph().getEndpoints(currentSourceEdge).getSecond();
121                 Collection<E> outs = getGraph().getOutEdges(newDestVertex);
122                 for (E currentDestEdge : outs) {
123                 	V destEdgeRoot = this.roots.get(currentDestEdge);
124                 	V destEdgeDest = getGraph().getEndpoints(currentDestEdge).getSecond();
125 
126                     if (currentSourceEdge == virtualSinkEdge) {
127                         newEdges.add(currentDestEdge);
128                         continue;
129                     }
130                     if (destEdgeRoot == root) {
131                         continue;
132                     }
133                     if (destEdgeDest == getGraph().getEndpoints(currentSourceEdge).getFirst()) {//currentSourceEdge.getSource()) {
134                         continue;
135                     }
136                     Set<Number> pathsSeen = this.pathsSeenMap.get(destEdgeDest);
137 
138                     if (pathsSeen == null) {
139                         newVertexEncountered(sourcePathIndex.intValue(), destEdgeDest, root);
140                     } else if (roots.get(destEdgeDest) != root) {
141                         roots.put(destEdgeDest,root);
142                         pathsSeen.clear();
143                         pathsSeen.add(sourcePathIndex);
144                     } else if (!pathsSeen.contains(sourcePathIndex)) {
145                         pathsSeen.add(sourcePathIndex);
146                     } else {
147                         continue;
148                     }
149 
150                     this.pathIndices.put(currentDestEdge, sourcePathIndex);
151                     this.roots.put(currentDestEdge, root);
152                     newEdges.add(currentDestEdge);
153                 }
154             }
155 
156             edges = newEdges;
157             currentDepth++;
158         }
159 
160         getGraph().removeVertex(virtualNode);
161     }
162 
163     private void newVertexEncountered(int sourcePathIndex, V dest, V root) {
164         Set<Number> pathsSeen = new HashSet<Number>();
165         pathsSeen.add(sourcePathIndex);
166         this.pathsSeenMap.put(dest, pathsSeen);
167         roots.put(dest, root);
168     }
169 
170     @Override
171     public void step() {
172         for (V v : mPriors) {
173             computeWeightedPathsFromSource(v, mMaxDepth);
174         }
175 
176         normalizeRankings();
177 //        return 0;
178     }
179     
180     /**
181      * Given a node, returns the corresponding rank score. This implementation of <code>getRankScore</code> assumes
182      * the decoration representing the rank score is of type <code>MutableDouble</code>.
183      * @return  the rank score for this node
184      */
185     @Override
186     public String getRankScoreKey() {
187         return WEIGHTED_NIPATHS_KEY;
188     }
189 
190     @Override
191     protected void onFinalize(Object udc) {
192     	pathIndices.remove(udc);
193     	roots.remove(udc);
194     	pathsSeenMap.remove(udc);
195     }
196 }