View Javadoc
1   /*
2    * Created on Jul 6, 2007
3    *
4    * Copyright (c) 2007, The JUNG Authors 
5    *
6    * All rights reserved.
7    *
8    * This software is open-source under the BSD license; see either
9    * "license.txt" or
10   * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
11   */
12  package edu.uci.ics.jung.algorithms.scoring;
13  
14  import java.util.HashMap;
15  import java.util.Map;
16  
17  import com.google.common.base.Function;
18  
19  import edu.uci.ics.jung.algorithms.scoring.util.DelegateToEdgeTransformer;
20  import edu.uci.ics.jung.algorithms.scoring.util.VEPair;
21  import edu.uci.ics.jung.algorithms.util.IterativeContext;
22  import edu.uci.ics.jung.graph.Hypergraph;
23  
24  /**
25   * An abstract class for algorithms that assign scores to vertices based on iterative methods.
26   * Generally, any (concrete) subclass will function by creating an instance, and then either calling
27   * <code>evaluate</code> (if the user wants to iterate until the algorithms is 'done') or 
28   * repeatedly call <code>step</code> (if the user wants to observe the values at each step).
29   */
30  public abstract class AbstractIterativeScorer<V,E,T> implements IterativeContext, VertexScorer<V,T>
31  {
32      /**
33       * Maximum number of iterations to use before terminating.  Defaults to 100.
34       */
35      protected int max_iterations;
36      
37      /**
38       * Minimum change from one step to the next; if all changes are &le; tolerance, 
39       * no further updates will occur.
40       * Defaults to 0.001.
41       */
42      protected double tolerance;
43      
44      /**
45       * The graph on which the calculations are to be made.
46       */
47      protected Hypergraph<V,E> graph;
48      
49      /**
50       * The total number of iterations used so far.
51       */
52      protected int total_iterations;
53      
54      /**
55       * The edge weights used by this algorithm.
56       */
57      protected Function<VEPair<V,E>, ? extends Number> edge_weights;
58      
59      /**
60       * Indicates whether the output and current values are in a 'swapped' state.
61       * Intended for internal use only.
62       */
63      protected boolean output_reversed;
64      
65      /**
66       * The map in which the output values are stored.
67       */
68      private Map<V, T> output;
69      
70      /**
71       * The map in which the current values are stored.
72       */
73      private Map<V, T> current_values;
74      
75      /**
76       * A flag representing whether this instance tolerates disconnected graphs.
77       * Instances that do not accept disconnected graphs may have unexpected behavior
78       * on disconnected graphs; they are not guaranteed to do an explicit check.
79       * Defaults to true.
80       */
81      private boolean accept_disconnected_graph;
82  
83  
84      protected boolean hyperedges_are_self_loops = false;
85  
86      /**
87       * Sets the output value for this vertex.
88       * @param v the vertex whose output value is to be set
89       * @param value the value to set
90       */
91      protected void setOutputValue(V v, T value)
92      {
93          output.put(v, value);
94      }
95      
96      /**
97       * Gets the output value for this vertex.
98       * @param v the vertex whose output value is to be retrieved
99       * @return the output value for this vertex
100      */
101     protected T getOutputValue(V v)
102     {
103         return output.get(v);
104     }
105     
106     /**
107      * Gets the current value for this vertex
108      * @param v the vertex whose current value is to be retrieved
109      * @return the current value for this vertex
110      */
111     protected T getCurrentValue(V v)
112     {
113         return current_values.get(v);
114     }
115     
116     /**
117      * Sets the current value for this vertex.
118      * @param v the vertex whose current value is to be set
119      * @param value the current value to set
120      */
121     protected void setCurrentValue(V v, T value)
122     {
123         current_values.put(v, value);
124     }
125     
126     /**
127      * The largest change seen so far among all vertex scores.
128      */
129     protected double max_delta;
130     
131     /**
132      * Creates an instance for the specified graph and edge weights.
133      * @param g the graph for which the instance is to be created
134      * @param edge_weights the edge weights for this instance
135      */
136     public AbstractIterativeScorer(Hypergraph<V,E> g, 
137     		Function<? super E, ? extends Number> edge_weights)
138     {
139         this.graph = g;
140         this.max_iterations = 100;
141         this.tolerance = 0.001;
142         this.accept_disconnected_graph = true;
143         setEdgeWeights(edge_weights);
144     }
145     
146     /**
147      * Creates an instance for the specified graph <code>g</code>.
148      * NOTE: This constructor does not set the internal 
149      * <code>edge_weights</code> variable.  If this variable is used by 
150      * the subclass which invoked this constructor, it must be initialized
151      * by that subclass.
152      * @param g the graph for which the instance is to be created
153      */
154     public AbstractIterativeScorer(Hypergraph<V,E> g)
155     {
156     	this.graph = g;
157         this.max_iterations = 100;
158         this.tolerance = 0.001;
159         this.accept_disconnected_graph = true;
160     }
161     
162     /**
163      * Initializes the internal state for this instance.
164      */
165     protected void initialize()
166     {
167         this.total_iterations = 0;
168         this.max_delta = Double.MIN_VALUE;
169         this.output_reversed = true;
170         this.current_values = new HashMap<V, T>();
171         this.output = new HashMap<V, T>();
172     }
173     
174     /**
175      * Steps through this scoring algorithm until a termination condition is reached.
176      */
177     public void evaluate()
178     {
179         do
180             step();
181         while (!done());
182     }
183     
184     /**
185      * Returns true if the total number of iterations is greater than or equal to 
186      * <code>max_iterations</code>
187      * or if the maximum value change observed is less than <code>tolerance</code>.
188      */
189     public boolean done()
190     {
191         return total_iterations >= max_iterations || max_delta < tolerance;
192     }
193 
194     /**
195      * Performs one step of this algorithm; updates the state (value) for each vertex.
196      */
197     public void step()
198     {
199         swapOutputForCurrent();
200         
201         for (V v : graph.getVertices())
202         {
203             double diff = update(v);
204             updateMaxDelta(v, diff);
205         }
206         total_iterations++;
207         afterStep();
208     }
209 
210     /**
211      * 
212      */
213     protected void swapOutputForCurrent()
214     {
215         Map<V, T> tmp = output;
216         output = current_values;
217         current_values = tmp;
218         output_reversed = !output_reversed;
219     }
220 
221     /**
222      * Updates the value for <code>v</code>.
223      * @param v the vertex whose value is to be updated
224      * @return the updated value
225      */
226     protected abstract double update(V v);
227 
228     protected void updateMaxDelta(V v, double diff)
229     {
230         max_delta = Math.max(max_delta, diff);
231     }
232     
233     protected void afterStep() {}
234     
235     public T getVertexScore(V v)
236     {
237         if (!graph.containsVertex(v))
238             throw new IllegalArgumentException("Vertex " + v + " not an element of this graph");
239         
240         return output.get(v);
241     }
242 
243     /**
244      * Returns the maximum number of iterations that this instance will use.
245      * @return the maximum number of iterations that <code>evaluate</code> will use
246      * prior to terminating
247      */
248     public int getMaxIterations()
249     {
250         return max_iterations;
251     }
252 
253     /**
254      * Returns the number of iterations that this instance has used so far.
255      * @return the number of iterations that this instance has used so far
256      */
257     public int getIterations()
258     {
259     	return total_iterations;
260     }
261     
262     /**
263      * Sets the maximum number of times that <code>evaluate</code> will call <code>step</code>.
264      * @param max_iterations the maximum 
265      */
266     public void setMaxIterations(int max_iterations)
267     {
268         this.max_iterations = max_iterations;
269     }
270 
271     /**
272      * Gets the size of the largest change (difference between the current and previous values)
273      * for any vertex that can be tolerated.  Once all changes are less than this value, 
274      * <code>evaluate</code> will terminate.
275      * @return the size of the largest change that evaluate() will permit
276      */
277     public double getTolerance()
278     {
279         return tolerance;
280     }
281 
282     /**
283      * Sets the size of the largest change (difference between the current and previous values)
284      * for any vertex that can be tolerated.
285      * @param tolerance the size of the largest change that evaluate() will permit
286      */
287     public void setTolerance(double tolerance)
288     {
289         this.tolerance = tolerance;
290     }
291     
292     /**
293      * Returns the Function that this instance uses to associate edge weights with each edge.
294      * @return the Function that associates an edge weight with each edge
295      */
296     public Function<VEPair<V,E>, ? extends Number> getEdgeWeights()
297     {
298         return edge_weights;
299     }
300 
301     /**
302      * Sets the Function that this instance uses to associate edge weights with each edge
303      * @param edge_weights the Function to use to associate an edge weight with each edge
304      * @see edu.uci.ics.jung.algorithms.scoring.util.UniformDegreeWeight
305      */
306     public void setEdgeWeights(Function<? super E, ? extends Number> edge_weights)
307     {
308         this.edge_weights = new DelegateToEdgeTransformer<V,E>(edge_weights);
309     }
310     
311     /**
312      * Gets the edge weight for <code>e</code> in the context of its (incident) vertex <code>v</code>.
313      * @param v the vertex incident to e as a context in which the edge weight is to be calculated
314      * @param e the edge whose weight is to be returned
315      * @return the edge weight for <code>e</code> in the context of its (incident) vertex <code>v</code>
316      */
317     protected Number getEdgeWeight(V v, E e)
318     {
319         return edge_weights.apply(new VEPair<V,E>(v,e));
320     }
321     
322     /**
323      * Collects the 'potential' from v (its current value) if it has no outgoing edges; this
324      * can then be redistributed among the other vertices as a means of normalization.
325      * @param v the vertex whose potential is being collected
326      */
327     protected void collectDisappearingPotential(V v) {}
328 
329     /**
330      * Specifies whether this instance should accept vertices with no outgoing edges.
331      * @param accept true if this instance should accept vertices with no outgoing edges, false otherwise
332      */
333     public void acceptDisconnectedGraph(boolean accept)
334     {
335         this.accept_disconnected_graph = accept;
336     }
337     
338     /**
339      * Returns true if this instance accepts vertices with no outgoing edges, and false otherwise.
340      * @return true if this instance accepts vertices with no outgoing edges, otherwise false
341      */
342     public boolean isDisconnectedGraphOK()
343     {
344         return this.accept_disconnected_graph;
345     }
346     
347     /**
348      * Specifies whether hyperedges are to be treated as self-loops.  If they
349      * are, then potential will flow along a hyperedge a vertex to itself, 
350      * just as it does to all other vertices incident to that hyperedge. 
351      * @param arg if {@code true}, hyperedges are treated as self-loops
352      */
353     public void setHyperedgesAreSelfLoops(boolean arg) 
354     {
355     	this.hyperedges_are_self_loops = arg;
356     }
357 
358     /**
359      * Returns the effective number of vertices incident to this edge.  If
360      * the graph is a binary relation or if hyperedges are treated as self-loops,
361      * the value returned is {@code graph.getIncidentCount(e)}; otherwise it is
362      * {@code graph.getIncidentCount(e) - 1}.
363      * @param e the edge whose incident edge count is requested
364      * @return the edge count, adjusted based on how hyperedges are treated
365      */
366     protected int getAdjustedIncidentCount(E e) 
367     {
368         return graph.getIncidentCount(e) - (hyperedges_are_self_loops ? 0 : 1);
369     }
370 }