View Javadoc
1   /*
2    * Created on Jul 9, 2005
3    *
4    * Copyright (c) 2005, 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.shortestpath;
13  
14  import java.util.Collection;
15  import java.util.Comparator;
16  import java.util.HashMap;
17  import java.util.HashSet;
18  import java.util.LinkedHashMap;
19  import java.util.Map;
20  import java.util.Set;
21  
22  import com.google.common.base.Function;
23  import com.google.common.base.Functions;
24  
25  import edu.uci.ics.jung.algorithms.util.BasicMapEntry;
26  import edu.uci.ics.jung.algorithms.util.MapBinaryHeap;
27  import edu.uci.ics.jung.graph.Graph;
28  import edu.uci.ics.jung.graph.Hypergraph;
29  
30  /**
31   * <p>Calculates distances in a specified graph, using  
32   * Dijkstra's single-source-shortest-path algorithm.  All edge weights
33   * in the graph must be nonnegative; if any edge with negative weight is 
34   * found in the course of calculating distances, an 
35   * <code>IllegalArgumentException</code> will be thrown.
36   * (Note: this exception will only be thrown when such an edge would be
37   * used to update a given tentative distance;
38   * the algorithm does not check for negative-weight edges "up front".)
39   * 
40   * <p>Distances and partial results are optionally cached (by this instance)
41   * for later reference.  Thus, if the 10 closest vertices to a specified source 
42   * vertex are known, calculating the 20 closest vertices does not require 
43   * starting Dijkstra's algorithm over from scratch.
44   * 
45   * <p>Distances are stored as double-precision values.  
46   * If a vertex is not reachable from the specified source vertex, no 
47   * distance is stored.  <b>This is new behavior with version 1.4</b>;
48   * the previous behavior was to store a value of 
49   * <code>Double.POSITIVE_INFINITY</code>.  This change gives the algorithm
50   * an approximate complexity of O(kD log k), where k is either the number of
51   * requested targets or the number of reachable vertices (whichever is smaller),
52   * and D is the average degree of a vertex.
53   * 
54   * <p> The elements in the maps returned by <code>getDistanceMap</code> 
55   * are ordered (that is, returned 
56   * by the iterator) by nondecreasing distance from <code>source</code>.
57   * 
58   * <p>Users are cautioned that distances calculated should be assumed to
59   * be invalidated by changes to the graph, and should invoke <code>reset()</code>
60   * when appropriate so that the distances can be recalculated.
61   * 
62   * @author Joshua O'Madadhain
63   * @author Tom Nelson converted to jung2
64   */
65  public class DijkstraDistance<V,E> implements Distance<V>
66  {
67      protected Hypergraph<V,E> g;
68      protected Function<? super E,? extends Number> nev;
69      protected Map<V,SourceData> sourceMap;   // a map of source vertices to an instance of SourceData
70      protected boolean cached;
71      protected double max_distance;
72      protected int max_targets;
73      
74      /**
75       * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
76       * the specified graph and the specified method of extracting weights 
77       * from edges, which caches results locally if and only if 
78       * <code>cached</code> is <code>true</code>.
79       * 
80       * @param g     the graph on which distances will be calculated
81       * @param nev   the class responsible for returning weights for edges
82       * @param cached    specifies whether the results are to be cached
83       */
84      public DijkstraDistance(Hypergraph<V,E> g, Function<? super E,? extends Number> nev, boolean cached) {
85          this.g = g;
86          this.nev = nev;
87          this.sourceMap = new HashMap<V,SourceData>();
88          this.cached = cached;
89          this.max_distance = Double.POSITIVE_INFINITY;
90          this.max_targets = Integer.MAX_VALUE;
91      }
92      
93      /**
94       * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
95       * the specified graph and the specified method of extracting weights 
96       * from edges, which caches results locally.
97       * 
98       * @param g     the graph on which distances will be calculated
99       * @param nev   the class responsible for returning weights for edges
100      */
101     public DijkstraDistance(Hypergraph<V,E> g, Function<? super E,? extends Number> nev) {
102         this(g, nev, true);
103     }
104     
105     /**
106      * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
107      * the specified unweighted graph (that is, all weights 1) which
108      * caches results locally.
109      * 
110      * @param g     the graph on which distances will be calculated
111      */ 
112     public DijkstraDistance(Graph<V,E> g) {
113         this(g, Functions.constant(1), true);
114     }
115 
116     /**
117      * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
118      * the specified unweighted graph (that is, all weights 1) which
119      * caches results locally.
120      * 
121      * @param g     the graph on which distances will be calculated
122      * @param cached    specifies whether the results are to be cached
123      */ 
124     public DijkstraDistance(Graph<V,E> g, boolean cached) {
125         this(g, Functions.constant(1), cached);
126     }
127     
128     /**
129      * Implements Dijkstra's single-source shortest-path algorithm for
130      * weighted graphs.  Uses a <code>MapBinaryHeap</code> as the priority queue, 
131      * which gives this algorithm a time complexity of O(m lg n) (m = # of edges, n = 
132      * # of vertices).
133      * This algorithm will terminate when any of the following have occurred (in order
134      * of priority):
135      * <ul>
136      * <li> the distance to the specified target (if any) has been found
137      * <li> no more vertices are reachable 
138      * <li> the specified # of distances have been found, or the maximum distance 
139      * desired has been exceeded
140      * <li> all distances have been found
141      * </ul>
142      * 
143      * @param source    the vertex from which distances are to be measured
144      * @param numDests  the number of distances to measure
145      * @param targets   the set of vertices to which distances are to be measured
146      * @return a mapping from vertex to the shortest distance from the source to each target
147      */
148     protected LinkedHashMap<V,Number> singleSourceShortestPath(V source, Collection<V> targets, int numDests)
149     {
150         SourceData sd = getSourceData(source);
151 
152         Set<V> to_get = new HashSet<V>();
153         if (targets != null) {
154             to_get.addAll(targets);
155             Set<V> existing_dists = sd.distances.keySet();
156             for(V o : targets) {
157                 if (existing_dists.contains(o))
158                     to_get.remove(o);
159             }
160         }
161         
162         // if we've exceeded the max distance or max # of distances we're willing to calculate, or
163         // if we already have all the distances we need, 
164         // terminate
165         if (sd.reached_max ||
166             (targets != null && to_get.isEmpty()) ||
167             (sd.distances.size() >= numDests))
168         {
169             return sd.distances;
170         }
171         
172         while (!sd.unknownVertices.isEmpty() && (sd.distances.size() < numDests || !to_get.isEmpty()))
173         {
174             Map.Entry<V,Number> p = sd.getNextVertex();
175             V v = p.getKey();
176             double v_dist = p.getValue().doubleValue();
177             to_get.remove(v);
178             if (v_dist > this.max_distance) 
179             {
180                 // we're done; put this vertex back in so that we're not including
181                 // a distance beyond what we specified
182                 sd.restoreVertex(v, v_dist);
183                 sd.reached_max = true;
184                 break;
185             }
186             sd.dist_reached = v_dist;
187 
188             if (sd.distances.size() >= this.max_targets)
189             {
190                 sd.reached_max = true;
191                 break;
192             }
193             
194             for (E e : getEdgesToCheck(v) )
195             {
196                 for (V w : g.getIncidentVertices(e))
197                 {
198                     if (!sd.distances.containsKey(w))
199                     {
200                         double edge_weight = nev.apply(e).doubleValue();
201                         if (edge_weight < 0)
202                             throw new IllegalArgumentException("Edges weights must be non-negative");
203                         double new_dist = v_dist + edge_weight;
204                         if (!sd.estimatedDistances.containsKey(w))
205                         {
206                             sd.createRecord(w, e, new_dist);
207                         }
208                         else
209                         {
210                             double w_dist = ((Double)sd.estimatedDistances.get(w)).doubleValue();
211                             if (new_dist < w_dist) // update tentative distance & path for w
212                                 sd.update(w, e, new_dist);
213                         }
214                     }
215                 }
216             }
217         }
218         return sd.distances;
219     }
220 
221     protected SourceData getSourceData(V source)
222     {
223         SourceData sd = sourceMap.get(source);
224         if (sd == null)
225             sd = new SourceData(source);
226         return sd;
227     }
228     
229     /**
230      * Returns the set of edges incident to <code>v</code> that should be tested.
231      * By default, this is the set of outgoing edges for instances of <code>Graph</code>,
232      * the set of incident edges for instances of <code>Hypergraph</code>,
233      * and is otherwise undefined.
234      * @param v the vertex whose edges are to be checked
235      * @return the set of edges incident to {@code v} that should be tested
236      */
237     protected Collection<E> getEdgesToCheck(V v)
238     {
239         if (g instanceof Graph)
240             return ((Graph<V,E>)g).getOutEdges(v);
241         else
242             return g.getIncidentEdges(v);
243 
244     }
245 
246     
247     /**
248      * Returns the length of a shortest path from the source to the target vertex,
249      * or null if the target is not reachable from the source.
250      * If either vertex is not in the graph for which this instance
251      * was created, throws <code>IllegalArgumentException</code>.
252      * 
253      * @param source the vertex from which the distance to {@code target} is to be measured
254      * @param target the vertex to which the distance from {@code source} is to be measured
255      * @return the distance between {@code source} and {@code target}
256      * 
257      * @see #getDistanceMap(Object)
258      * @see #getDistanceMap(Object,int)
259      */
260     public Number getDistance(V source, V target)
261     {
262         if (g.containsVertex(target) == false)
263             throw new IllegalArgumentException("Specified target vertex " + 
264                     target + " is not part of graph " + g);
265         if (g.containsVertex(source) == false)
266             throw new IllegalArgumentException("Specified source vertex " + 
267                     source + " is not part of graph " + g);
268         
269         Set<V> targets = new HashSet<V>();
270         targets.add(target);
271         Map<V,Number> distanceMap = getDistanceMap(source, targets);
272         return distanceMap.get(target);
273     }
274     
275 
276     /**
277      * Returns a {@code Map} from each element {@code t} of {@code targets} to the 
278      * shortest-path distance from {@code source} to {@code t}. 
279      * @param source the vertex from which the distance to each target is to be measured
280      * @param targets the vertices to which the distance from the source is to be measured
281      * @return {@code Map} from each element of {@code targets} to its distance from {@code source}
282      */
283     public Map<V,Number> getDistanceMap(V source, Collection<V> targets)
284     {
285        if (g.containsVertex(source) == false)
286             throw new IllegalArgumentException("Specified source vertex " + 
287                     source + " is not part of graph " + g);
288        if (targets.size() > max_targets)
289             throw new IllegalArgumentException("size of target set exceeds maximum " +
290                     "number of targets allowed: " + this.max_targets);
291         
292         Map<V,Number> distanceMap = 
293         	singleSourceShortestPath(source, targets, 
294         			Math.min(g.getVertexCount(), max_targets));
295         if (!cached)
296             reset(source);
297         
298         return distanceMap;
299     }
300     
301     /**
302      * <p>Returns a <code>LinkedHashMap</code> which maps each vertex 
303      * in the graph (including the <code>source</code> vertex) 
304      * to its distance from the <code>source</code> vertex.
305      * The map's iterator will return the elements in order of 
306      * increasing distance from <code>source</code>.
307      * 
308      * <p>The size of the map returned will be the number of 
309      * vertices reachable from <code>source</code>.
310      * 
311      * @see #getDistanceMap(Object,int)
312      * @see #getDistance(Object,Object)
313      * @param source    the vertex from which distances are measured
314      * @return a mapping from each vertex in the graph to its distance from {@code source}
315      */
316     public Map<V,Number> getDistanceMap(V source)
317     {
318         return getDistanceMap(source, Math.min(g.getVertexCount(), max_targets));
319     }
320     
321 
322 
323     /**
324      * <p>Returns a <code>LinkedHashMap</code> which maps each of the closest 
325      * <code>numDist</code> vertices to the <code>source</code> vertex 
326      * in the graph (including the <code>source</code> vertex) 
327      * to its distance from the <code>source</code> vertex.  Throws 
328      * an <code>IllegalArgumentException</code> if <code>source</code>
329      * is not in this instance's graph, or if <code>numDests</code> is 
330      * either less than 1 or greater than the number of vertices in the 
331      * graph.
332      * 
333      * <p>The size of the map returned will be the smaller of 
334      * <code>numDests</code> and the number of vertices reachable from
335      * <code>source</code>. 
336      * 
337      * @see #getDistanceMap(Object)
338      * @see #getDistance(Object,Object)
339      * @param source    the vertex from which distances are measured
340      * @param numDests  the number of vertices for which to measure distances
341      * @return a mapping from the {@code numDests} vertices in the graph
342      *     closest to {@code source}, to their distance from {@code source}
343      *   
344      */
345     public LinkedHashMap<V,Number> getDistanceMap(V source, int numDests)
346     {
347 
348     	if(g.getVertices().contains(source) == false) {
349             throw new IllegalArgumentException("Specified source vertex " + 
350                     source + " is not part of graph " + g);
351     		
352     	}
353         if (numDests < 1 || numDests > g.getVertexCount())
354             throw new IllegalArgumentException("numDests must be >= 1 " + 
355                 "and <= g.numVertices()");
356 
357         if (numDests > max_targets)
358             throw new IllegalArgumentException("numDests must be <= the maximum " +
359                     "number of targets allowed: " + this.max_targets);
360             
361         LinkedHashMap<V,Number> distanceMap = 
362         	singleSourceShortestPath(source, null, numDests);
363                 
364         if (!cached)
365             reset(source);
366         
367         return distanceMap;        
368     }
369     
370     /**
371      * Allows the user to specify the maximum distance that this instance will calculate.
372      * Any vertices past this distance will effectively be unreachable from the source, in
373      * the sense that the algorithm will not calculate the distance to any vertices which
374      * are farther away than this distance.  A negative value for <code>max_dist</code> 
375      * will ensure that no further distances are calculated.
376      * 
377      * <p>This can be useful for limiting the amount of time and space used by this algorithm
378      * if the graph is very large.
379      * 
380      * <p>Note: if this instance has already calculated distances greater than <code>max_dist</code>,
381      * and the results are cached, those results will still be valid and available; this limit
382      * applies only to subsequent distance calculations.
383      * 
384      * @param max_dist the maximum distance that this instance will calculate
385      * 
386      * @see #setMaxTargets(int)
387      */
388     public void setMaxDistance(double max_dist)
389     {
390         this.max_distance = max_dist;
391         for (V v : sourceMap.keySet())
392         {
393             SourceData sd = sourceMap.get(v);
394             sd.reached_max = (this.max_distance <= sd.dist_reached) || (sd.distances.size() >= max_targets);
395         }
396     }
397        
398     /**
399      * Allows the user to specify the maximum number of target vertices per source vertex 
400      * for which this instance will calculate distances.  Once this threshold is reached, 
401      * any further vertices will effectively be unreachable from the source, in
402      * the sense that the algorithm will not calculate the distance to any more vertices.  
403      * A negative value for <code>max_targets</code> will ensure that no further distances are calculated.
404      * 
405      * <p>This can be useful for limiting the amount of time and space used by this algorithm
406      * if the graph is very large.
407      * 
408      * <p>Note: if this instance has already calculated distances to a greater number of 
409      * targets than <code>max_targets</code>, and the results are cached, those results 
410      * will still be valid and available; this limit applies only to subsequent distance 
411      * calculations.
412      * 
413      * @param max_targets the maximum number of targets for which this instance will calculate
414      *     distances
415      * 
416      * @see #setMaxDistance(double)
417      */
418     public void setMaxTargets(int max_targets)
419     {
420         this.max_targets = max_targets;
421         for (V v : sourceMap.keySet())
422         {
423             SourceData sd = sourceMap.get(v);
424             sd.reached_max = (this.max_distance <= sd.dist_reached) || (sd.distances.size() >= max_targets);
425         }
426     }
427     
428     /**
429      * Clears all stored distances for this instance.  
430      * Should be called whenever the graph is modified (edge weights 
431      * changed or edges added/removed).  If the user knows that
432      * some currently calculated distances are unaffected by a
433      * change, <code>reset(V)</code> may be appropriate instead.
434      * 
435      * @see #reset(Object)
436      */
437     public void reset()
438     {
439         sourceMap = new HashMap<V,SourceData>();
440     }
441         
442     /**
443      * Specifies whether or not this instance of <code>DijkstraShortestPath</code>
444      * should cache its results (final and partial) for future reference.
445      * 
446      * @param enable    <code>true</code> if the results are to be cached, and
447      *                  <code>false</code> otherwise
448      */
449     public void enableCaching(boolean enable)
450     {
451         this.cached = enable;
452     }
453     
454     /**
455      * Clears all stored distances for the specified source vertex 
456      * <code>source</code>.  Should be called whenever the stored distances
457      * from this vertex are invalidated by changes to the graph.
458      * 
459      * @param source the vertex for which stored distances should be cleared
460      * 
461      * @see #reset()
462      */
463     public void reset(V source)
464     {
465         sourceMap.put(source, null);
466     }
467 
468     /**
469      * Compares according to distances, so that the BinaryHeap knows how to 
470      * order the tree.  
471      */
472     protected static class VertexComparator<V> implements Comparator<V>
473     {
474         private Map<V,Number> distances;
475         
476         protected VertexComparator(Map<V,Number> distances)
477         {
478             this.distances = distances;
479         }
480 
481         public int compare(V o1, V o2)
482         {
483             return ((Double) distances.get(o1)).compareTo((Double) distances.get(o2));
484         }
485     }
486     
487     /**
488      * For a given source vertex, holds the estimated and final distances, 
489      * tentative and final assignments of incoming edges on the shortest path from
490      * the source vertex, and a priority queue (ordered by estimated distance)
491      * of the vertices for which distances are unknown.
492      * 
493      * @author Joshua O'Madadhain
494      */
495     protected class SourceData
496     {
497         protected LinkedHashMap<V,Number> distances;
498         protected Map<V,Number> estimatedDistances;
499         protected MapBinaryHeap<V> unknownVertices;
500         protected boolean reached_max = false;
501         protected double dist_reached = 0;
502 
503         protected SourceData(V source)
504         {
505             distances = new LinkedHashMap<V,Number>();
506             estimatedDistances = new HashMap<V,Number>();
507             unknownVertices = new MapBinaryHeap<V>(new VertexComparator<V>(estimatedDistances));
508             
509             sourceMap.put(source, this);
510             
511             // initialize priority queue
512             estimatedDistances.put(source, new Double(0)); // distance from source to itself is 0
513             unknownVertices.add(source);
514             reached_max = false;
515             dist_reached = 0;
516         }
517         
518         protected Map.Entry<V,Number> getNextVertex()
519         {
520             V v = unknownVertices.remove();
521             Double dist = (Double)estimatedDistances.remove(v);
522             distances.put(v, dist);
523             return new BasicMapEntry<V,Number>(v, dist);
524         }
525         
526         protected void update(V dest, E tentative_edge, double new_dist)
527         {
528             estimatedDistances.put(dest, new_dist);
529             unknownVertices.update(dest);
530         }
531         
532         protected void createRecord(V w, E e, double new_dist)
533         {
534             estimatedDistances.put(w, new_dist);
535             unknownVertices.add(w);
536         }
537         
538         protected void restoreVertex(V v, double dist) 
539         {
540             estimatedDistances.put(v, dist);
541             unknownVertices.add(v);
542             distances.remove(v);
543         }
544     }
545 }