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 ≤ 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 }