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.text.DecimalFormat;
13  import java.text.Format;
14  import java.util.ArrayList;
15  import java.util.Collection;
16  import java.util.Collections;
17  import java.util.HashMap;
18  import java.util.List;
19  import java.util.Map;
20  
21  import com.google.common.cache.CacheBuilder;
22  import com.google.common.cache.CacheLoader;
23  import com.google.common.cache.LoadingCache;
24  
25  import edu.uci.ics.jung.algorithms.util.IterativeProcess;
26  import edu.uci.ics.jung.graph.Graph;
27  
28  /**
29   * Abstract class for algorithms that rank nodes or edges by some "importance" metric. Provides a common set of
30   * services such as:
31   * <ul>
32   *  <li> storing rank scores</li>
33   *  <li> getters and setters for rank scores</li>
34   *  <li> computing default edge weights</li>
35   *  <li> normalizing default or user-provided edge transition weights </li>
36   *  <li> normalizing rank scores</li>
37   *  <li> automatic cleanup of decorations</li>
38   *  <li> creation of Ranking list</li>
39   * <li>print rankings in sorted order by rank</li>
40   * </ul>
41   * <p>
42   * By default, all rank scores are removed from the vertices (or edges) being ranked.
43   * @author Scott White
44   */
45  public abstract class AbstractRanker<V,E> extends IterativeProcess {
46      private Graph<V,E> mGraph;
47      private List<Ranking<?>> mRankings;
48      private boolean mRemoveRankScoresOnFinalize;
49      private boolean mRankNodes;
50      private boolean mRankEdges;
51      private boolean mNormalizeRankings;
52      protected LoadingCache<Object, Map<V, Number>> vertexRankScores
53      	= CacheBuilder.newBuilder().build(new CacheLoader<Object, Map<V, Number>>() {
54  	    	public Map<V, Number> load(Object o) {
55  	    		return new HashMap<V, Number>();
56  	    	}
57  	});
58      protected LoadingCache<Object, Map<E, Number>> edgeRankScores
59      	= CacheBuilder.newBuilder().build(new CacheLoader<Object, Map<E, Number>>() {
60  	    	public Map<E, Number> load(Object o) {
61  	    		return new HashMap<E, Number>();
62  	    	}
63      });
64      
65      private Map<E,Number> edgeWeights = new HashMap<E,Number>();
66  
67      protected void initialize(Graph<V,E> graph, boolean isNodeRanker, 
68          boolean isEdgeRanker) {
69          if (!isNodeRanker && !isEdgeRanker)
70              throw new IllegalArgumentException("Must rank edges, vertices, or both");
71          mGraph = graph;
72          mRemoveRankScoresOnFinalize = true;
73          mNormalizeRankings = true;
74          mRankNodes = isNodeRanker;
75          mRankEdges = isEdgeRanker;
76      }
77      
78      /**
79  	 * @return all rankScores
80  	 */
81  	public Map<Object,Map<V, Number>> getVertexRankScores() {
82  		return vertexRankScores.asMap();
83  	}
84  
85  	public Map<Object,Map<E, Number>> getEdgeRankScores() {
86  		return edgeRankScores.asMap();
87  	}
88  
89      /**
90       * @param key the rank score key whose scores are to be retrieved
91  	 * @return the rank scores for the specified key
92  	 */
93  	public Map<V, Number> getVertexRankScores(Object key) {
94  		return vertexRankScores.getUnchecked(key);
95  	}
96  
97  	public Map<E, Number> getEdgeRankScores(Object key) {
98  		return edgeRankScores.getUnchecked(key);
99  	}
100 
101 	protected Collection<V> getVertices() {
102         return mGraph.getVertices();
103     }
104 
105 	protected int getVertexCount() {
106         return mGraph.getVertexCount();
107     }
108 
109     protected Graph<V,E> getGraph() {
110         return mGraph;
111     }
112 
113     @Override
114     public void reset() {
115     }
116 
117     /**
118      * @return <code>true</code> if this ranker ranks nodes, and 
119      * <code>false</code> otherwise.
120      */
121     public boolean isRankingNodes() {
122         return mRankNodes;
123     }
124 
125     /**
126      * @return <code>true</code> if this ranker ranks edges, and 
127      * <code>false</code> otherwise.
128      */
129     public boolean isRankingEdges() {
130         return mRankEdges;
131     }
132     
133     /**
134      * Instructs the ranker whether or not it should remove the rank scores from the nodes (or edges) once the ranks
135      * have been computed.
136      * @param removeRankScoresOnFinalize <code>true</code> if the rank scores are to be removed, <code>false</code> otherwise
137      */
138     public void setRemoveRankScoresOnFinalize(boolean removeRankScoresOnFinalize) {
139         this.mRemoveRankScoresOnFinalize = removeRankScoresOnFinalize;
140     }
141 
142     protected void onFinalize(Object e) {}
143     
144     /**
145      * The user datum key used to store the rank score.
146      * @return the key
147      */
148     abstract public Object getRankScoreKey();
149 
150 
151 	@SuppressWarnings("unchecked")
152 	@Override
153     protected void finalizeIterations() {
154         List<Ranking<?>> sortedRankings = new ArrayList<Ranking<?>>();
155 
156         int id = 1;
157         if (mRankNodes) {
158             for (V currentVertex : getVertices()) {
159                 Ranking<V> ranking = new Ranking<V>(id,getVertexRankScore(currentVertex),currentVertex);
160                 sortedRankings.add(ranking);
161                 if (mRemoveRankScoresOnFinalize) {
162                 	this.vertexRankScores.getUnchecked(getRankScoreKey()).remove(currentVertex);
163                 }
164                 id++;
165                 onFinalize(currentVertex);
166             }
167         }
168         if (mRankEdges) {
169             for (E currentEdge : mGraph.getEdges()) {
170 
171                 Ranking<E> ranking = new Ranking<E>(id,getEdgeRankScore(currentEdge),currentEdge);
172                 sortedRankings.add(ranking);
173                 if (mRemoveRankScoresOnFinalize) {
174                 	this.edgeRankScores.getUnchecked(getRankScoreKey()).remove(currentEdge);
175                 }
176                 id++;
177                 onFinalize(currentEdge);
178             }
179         }
180 
181         mRankings = sortedRankings;
182         Collections.sort(mRankings);
183     }
184 
185     /**
186      * Retrieves the list of ranking instances in descending sorted order by rank score
187      * If the algorithm is ranking edges, the instances will be of type <code>EdgeRanking</code>, otherwise
188      * if the algorithm is ranking nodes the instances will be of type <code>NodeRanking</code>
189      * @return  the list of rankings
190      */
191     public List<Ranking<?>> getRankings() {
192         return mRankings;
193     }
194 
195     /**
196      * Return a list of the top k rank scores.
197      * @param topKRankings the value of k to use
198      * @return list of rank scores
199      */
200     public List<Double> getRankScores(int topKRankings) {
201         List<Double> scores = new ArrayList<Double>();
202         int count=1;
203         for (Ranking<?> currentRanking : getRankings()) {
204             if (count > topKRankings) {
205                 return scores;
206             }
207             scores.add(currentRanking.rankScore);
208             count++;
209         }
210 
211         return scores;
212     }
213 
214     /**
215      * Given a node, returns the corresponding rank score. This is a default
216      * implementation of getRankScore which assumes the decorations are of type MutableDouble.
217      * This method only returns legal values if <code>setRemoveRankScoresOnFinalize(false)</code> was called
218      * prior to <code>evaluate()</code>.
219      * 
220      * @param v the node whose rank score is to be returned.
221      * @return  the rank score value
222      */
223     public double getVertexRankScore(V v) {
224         Number rankScore = vertexRankScores.getUnchecked(getRankScoreKey()).get(v);
225         if (rankScore != null) {
226             return rankScore.doubleValue();
227         } else {
228             throw new RuntimeException("setRemoveRankScoresOnFinalize(false) must be called before evaluate().");
229         }
230     }
231     
232     public double getVertexRankScore(V v, Object key) {
233     	return vertexRankScores.getUnchecked(key).get(v).doubleValue();
234     }
235 
236     public double getEdgeRankScore(E e) {
237         Number rankScore = edgeRankScores.getUnchecked(getRankScoreKey()).get(e);
238         if (rankScore != null) {
239             return rankScore.doubleValue();
240         } else {
241             throw new RuntimeException("setRemoveRankScoresOnFinalize(false) must be called before evaluate().");
242         }
243     }
244     
245     public double getEdgeRankScore(E e, Object key) {
246     	return edgeRankScores.getUnchecked(key).get(e).doubleValue();
247     }
248 
249     protected void setVertexRankScore(V v, double rankValue, Object key) {
250     	vertexRankScores.getUnchecked(key).put(v, rankValue);
251     }
252 
253     protected void setEdgeRankScore(E e, double rankValue, Object key) {
254 		edgeRankScores.getUnchecked(key).put(e, rankValue);
255     }
256 
257     protected void setVertexRankScore(V v, double rankValue) {
258     	setVertexRankScore(v,rankValue, getRankScoreKey());
259     }
260 
261     protected void setEdgeRankScore(E e, double rankValue) {
262     	setEdgeRankScore(e, rankValue, getRankScoreKey());
263     }
264 
265     protected void removeVertexRankScore(V v, Object key) {
266     	vertexRankScores.getUnchecked(key).remove(v);
267     }
268 
269     protected void removeEdgeRankScore(E e, Object key) {
270     	edgeRankScores.getUnchecked(key).remove(e);
271     }
272 
273     protected void removeVertexRankScore(V v) {
274     	vertexRankScores.getUnchecked(getRankScoreKey()).remove(v);
275     }
276 
277     protected void removeEdgeRankScore(E e) {
278     	edgeRankScores.getUnchecked(getRankScoreKey()).remove(e);
279     }
280 
281     protected double getEdgeWeight(E e) {
282     	return edgeWeights.get(e).doubleValue();
283     }
284 
285     protected void setEdgeWeight(E e, double weight) {
286     	edgeWeights.put(e, weight);
287     }
288     
289     public void setEdgeWeights(Map<E,Number> edgeWeights) {
290     	this.edgeWeights = edgeWeights;
291     }
292 
293     /**
294 	 * @return the edgeWeights
295 	 */
296 	public Map<E, Number> getEdgeWeights() {
297 		return edgeWeights;
298 	}
299 
300 	protected void assignDefaultEdgeTransitionWeights() {
301 
302         for (V currentVertex : getVertices()) {
303 
304             Collection<E> outgoingEdges = mGraph.getOutEdges(currentVertex);
305 
306             double numOutEdges = outgoingEdges.size();
307             for (E currentEdge : outgoingEdges) {
308                 setEdgeWeight(currentEdge,1.0/numOutEdges);
309             }
310         }
311     }
312 
313     protected void normalizeEdgeTransitionWeights() {
314 
315         for (V currentVertex : getVertices()) {
316 
317         	Collection<E> outgoingEdges = mGraph.getOutEdges(currentVertex);
318 
319             double totalEdgeWeight = 0;
320             for (E currentEdge : outgoingEdges) {
321                 totalEdgeWeight += getEdgeWeight(currentEdge);
322             }
323 
324             for (E currentEdge : outgoingEdges) {
325                 setEdgeWeight(currentEdge,getEdgeWeight(currentEdge)/totalEdgeWeight);
326             }
327         }
328     }
329 
330     protected void normalizeRankings() {
331         if (!mNormalizeRankings) {
332             return;
333         }
334         double totalWeight = 0;
335 
336         for (V currentVertex : getVertices()) {
337             totalWeight += getVertexRankScore(currentVertex);
338         }
339 
340         for (V currentVertex : getVertices()) {
341             setVertexRankScore(currentVertex,getVertexRankScore(currentVertex)/totalWeight);
342         }
343     }
344 
345     /**
346      * Print the rankings to standard out in descending order of rank score
347      * @param verbose if <code>true</code>, include information about the actual rank order as well as
348      * the original position of the vertex before it was ranked
349      * @param printScore if <code>true</code>, include the actual value of the rank score
350      */
351     public void printRankings(boolean verbose,boolean printScore) {
352             double total = 0;
353             Format formatter = new DecimalFormat("#0.#######");
354             int rank = 1;
355 
356             for (Ranking<?> currentRanking : getRankings()) {
357                 double rankScore = currentRanking.rankScore;
358                 if (verbose) {
359                     System.out.print("Rank " + rank + ": ");
360                     if (printScore) {
361                         System.out.print(formatter.format(rankScore));
362                     }
363                     System.out.print("\tVertex Id: " + currentRanking.originalPos);
364                         System.out.print(" (" + currentRanking.getRanked() + ")");
365                     System.out.println();
366                 } else {
367                     System.out.print(rank + "\t");
368                      if (printScore) {
369                         System.out.print(formatter.format(rankScore));
370                     }
371                     System.out.println("\t" + currentRanking.originalPos);
372 
373                 }
374                 total += rankScore;
375                 rank++;
376             }
377 
378             if (verbose) {
379                 System.out.println("Total: " + formatter.format(total));
380             }
381     }
382 
383     /**
384      * Allows the user to specify whether or not s/he wants the rankings to be normalized.
385      * In some cases, this will have no effect since the algorithm doesn't allow normalization
386      * as an option
387      * @param normalizeRankings {@code true} iff the ranking are to be normalized
388      */
389     public void setNormalizeRankings(boolean normalizeRankings) {
390         mNormalizeRankings = normalizeRankings;
391     }
392 }