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 }