Initial opendaylight infrastructure commit!!
[controller.git] / third-party / net.sf.jung2 / src / main / java / edu / uci / ics / jung / algorithms / shortestpath / DijkstraDistance.java
diff --git a/third-party/net.sf.jung2/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraDistance.java b/third-party/net.sf.jung2/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraDistance.java
new file mode 100644 (file)
index 0000000..3e99e16
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * Created on Jul 9, 2005
+ *
+ * Copyright (c) 2005, the JUNG Project and the Regents of the University 
+ * of California
+ * All rights reserved.
+ *
+ * This software is open-source under the BSD license; see either
+ * "license.txt" or
+ * http://jung.sourceforge.net/license.txt for a description.
+ */
+package edu.uci.ics.jung.algorithms.shortestpath;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections15.Transformer;
+import org.apache.commons.collections15.functors.ConstantTransformer;
+
+import edu.uci.ics.jung.algorithms.util.BasicMapEntry;
+import edu.uci.ics.jung.algorithms.util.MapBinaryHeap;
+import edu.uci.ics.jung.graph.Graph;
+import edu.uci.ics.jung.graph.Hypergraph;
+
+/**
+ * <p>Calculates distances in a specified graph, using  
+ * Dijkstra's single-source-shortest-path algorithm.  All edge weights
+ * in the graph must be nonnegative; if any edge with negative weight is 
+ * found in the course of calculating distances, an 
+ * <code>IllegalArgumentException</code> will be thrown.
+ * (Note: this exception will only be thrown when such an edge would be
+ * used to update a given tentative distance;
+ * the algorithm does not check for negative-weight edges "up front".)
+ * 
+ * <p>Distances and partial results are optionally cached (by this instance)
+ * for later reference.  Thus, if the 10 closest vertices to a specified source 
+ * vertex are known, calculating the 20 closest vertices does not require 
+ * starting Dijkstra's algorithm over from scratch.</p>
+ * 
+ * <p>Distances are stored as double-precision values.  
+ * If a vertex is not reachable from the specified source vertex, no 
+ * distance is stored.  <b>This is new behavior with version 1.4</b>;
+ * the previous behavior was to store a value of 
+ * <code>Double.POSITIVE_INFINITY</code>.  This change gives the algorithm
+ * an approximate complexity of O(kD log k), where k is either the number of
+ * requested targets or the number of reachable vertices (whichever is smaller),
+ * and D is the average degree of a vertex.</p>
+ * 
+ * <p> The elements in the maps returned by <code>getDistanceMap</code> 
+ * are ordered (that is, returned 
+ * by the iterator) by nondecreasing distance from <code>source</code>.</p>
+ * 
+ * <p>Users are cautioned that distances calculated should be assumed to
+ * be invalidated by changes to the graph, and should invoke <code>reset()</code>
+ * when appropriate so that the distances can be recalculated.</p>
+ * 
+ * @author Joshua O'Madadhain
+ * @author Tom Nelson converted to jung2
+ */
+public class DijkstraDistance<V,E> implements Distance<V>
+{
+    protected Hypergraph<V,E> g;
+    protected Transformer<E,? extends Number> nev;
+    protected Map<V,SourceData> sourceMap;   // a map of source vertices to an instance of SourceData
+    protected boolean cached;
+    protected double max_distance;
+    protected int max_targets;
+    
+    /**
+     * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
+     * the specified graph and the specified method of extracting weights 
+     * from edges, which caches results locally if and only if 
+     * <code>cached</code> is <code>true</code>.
+     * 
+     * @param g     the graph on which distances will be calculated
+     * @param nev   the class responsible for returning weights for edges
+     * @param cached    specifies whether the results are to be cached
+     */
+    public DijkstraDistance(Hypergraph<V,E> g, Transformer<E,? extends Number> nev, boolean cached) {
+        this.g = g;
+        this.nev = nev;
+        this.sourceMap = new HashMap<V,SourceData>();
+        this.cached = cached;
+        this.max_distance = Double.POSITIVE_INFINITY;
+        this.max_targets = Integer.MAX_VALUE;
+    }
+    
+    /**
+     * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
+     * the specified graph and the specified method of extracting weights 
+     * from edges, which caches results locally.
+     * 
+     * @param g     the graph on which distances will be calculated
+     * @param nev   the class responsible for returning weights for edges
+     */
+    public DijkstraDistance(Hypergraph<V,E> g, Transformer<E,? extends Number> nev) {
+        this(g, nev, true);
+    }
+    
+    /**
+     * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
+     * the specified unweighted graph (that is, all weights 1) which
+     * caches results locally.
+     * 
+     * @param g     the graph on which distances will be calculated
+     */ 
+    @SuppressWarnings("unchecked")
+    public DijkstraDistance(Hypergraph<V,E> g) {
+        this(g, new ConstantTransformer(1), true);
+    }
+
+    /**
+     * <p>Creates an instance of <code>DijkstraShortestPath</code> for 
+     * the specified unweighted graph (that is, all weights 1) which
+     * caches results locally.
+     * 
+     * @param g     the graph on which distances will be calculated
+     * @param cached    specifies whether the results are to be cached
+     */ 
+    @SuppressWarnings("unchecked")
+    public DijkstraDistance(Hypergraph<V,E> g, boolean cached) {
+        this(g, new ConstantTransformer(1), cached);
+    }
+
+    /**
+     * Implements Dijkstra's single-source shortest-path algorithm for
+     * weighted graphs.  Uses a <code>MapBinaryHeap</code> as the priority queue, 
+     * which gives this algorithm a time complexity of O(m lg n) (m = # of edges, n = 
+     * # of vertices).
+     * This algorithm will terminate when any of the following have occurred (in order
+     * of priority):
+     * <ul>
+     * <li> the distance to the specified target (if any) has been found
+     * <li> no more vertices are reachable 
+     * <li> the specified # of distances have been found, or the maximum distance 
+     * desired has been exceeded
+     * <li> all distances have been found
+     * </ul>
+     * 
+     * @param source    the vertex from which distances are to be measured
+     * @param numDests  the number of distances to measure
+     * @param targets   the set of vertices to which distances are to be measured
+     * @param regular   boolean is true if we want regular SP dijkstra. False for MT.
+     */
+    private LinkedHashMap<V,Number> singleSourceShortestPath(V source, Collection<V> targets, int numDests, boolean regular)
+    {
+        SourceData sd = getSourceData(source);
+
+        Set<V> to_get = new HashSet<V>();
+        if (targets != null) {
+            to_get.addAll(targets);
+            Set<V> existing_dists = sd.distances.keySet();
+            for(V o : targets) {
+                if (existing_dists.contains(o))
+                    to_get.remove(o);
+            }
+        }
+        
+        // if we've exceeded the max distance or max # of distances we're willing to calculate, or
+        // if we already have all the distances we need, 
+        // terminate
+        if (sd.reached_max ||
+            (targets != null && to_get.isEmpty()) ||
+            (sd.distances.size() >= numDests))
+        {
+            return sd.distances;
+        }
+        
+        while (!sd.unknownVertices.isEmpty() && (sd.distances.size() < numDests || !to_get.isEmpty()))
+        {
+            Map.Entry<V,Number> p = sd.getNextVertex();
+            V v = p.getKey();
+            double v_dist = p.getValue().doubleValue();
+            to_get.remove(v);
+            if (v_dist > this.max_distance) 
+            {
+                // we're done; put this vertex back in so that we're not including
+                // a distance beyond what we specified
+                sd.restoreVertex(v, v_dist);
+                sd.reached_max = true;
+                break;
+            }
+            sd.dist_reached = v_dist;
+
+            if (sd.distances.size() >= this.max_targets)
+            {
+                sd.reached_max = true;
+                break;
+            }
+            
+            for (E e : getEdgesToCheck(v) )
+            {
+                for (V w : g.getIncidentVertices(e))
+                {
+                    if (!sd.distances.containsKey(w))
+                    {
+                        double edge_weight = nev.transform(e).doubleValue();
+                        if (edge_weight < 0)
+                            throw new IllegalArgumentException("Edges weights must be non-negative");
+                        double new_dist;
+                        if (regular == true) {
+                          new_dist = v_dist + edge_weight;
+                        } else {
+                          if (v_dist <= edge_weight) {
+                            new_dist = edge_weight;
+                          } else {
+                            new_dist = v_dist;
+                          }
+                        }
+                        if (!sd.estimatedDistances.containsKey(w))
+                        {
+                            sd.createRecord(w, e, new_dist);
+                        }
+                        else
+                        {
+                            double w_dist = ((Double)sd.estimatedDistances.get(w)).doubleValue();
+                            if (new_dist < w_dist) // update tentative distance & path for w
+                                sd.update(w, e, new_dist);
+                        }
+                    }
+                }
+            }
+        }
+        return sd.distances;
+    }
+    
+    /**
+     * Implements Dijkstra's single-source shortest-path algorithm for
+     * weighted graphs.  Uses a <code>MapBinaryHeap</code> as the priority queue, 
+     * which gives this algorithm a time complexity of O(m lg n) (m = # of edges, n = 
+     * # of vertices).
+     * This algorithm will terminate when any of the following have occurred (in order
+     * of priority):
+     * <ul>
+     * <li> the distance to the specified target (if any) has been found
+     * <li> no more vertices are reachable 
+     * <li> the specified # of distances have been found, or the maximum distance 
+     * desired has been exceeded
+     * <li> all distances have been found
+     * </ul>
+     * 
+     * @param source    the vertex from which distances are to be measured
+     * @param numDests  the number of distances to measure
+     * @param targets   the set of vertices to which distances are to be measured
+     */
+    protected LinkedHashMap<V,Number> singleSourceShortestPath(V source, Collection<V> targets, int numDests)
+    {
+        return singleSourceShortestPath(source, targets, numDests, true);
+    }
+
+    /**
+     * Implements Dijkstra's single-source shortest-path algorithm for
+     * weighted graphs.  Uses a <code>MapBinaryHeap</code> as the priority queue, 
+     * which gives this algorithm a time complexity of O(m lg n) (m = # of edges, n = 
+     * # of vertices).
+     * This algorithm will terminate when any of the following have occurred (in order
+     * of priority):
+     * <ul>
+     * <li> the distance to the specified target (if any) has been found
+     * <li> no more vertices are reachable 
+     * <li> the specified # of distances have been found, or the maximum distance 
+     * desired has been exceeded
+     * <li> all distances have been found
+     * </ul>
+     * 
+     * @param source    the vertex from which distances are to be measured
+     * @param numDests  the number of distances to measure
+     * @param targets   the set of vertices to which distances are to be measured
+     */
+    protected LinkedHashMap<V,Number> singleSourceMaxThroughputPath(V source, Collection<V> targets, int numDests)
+    {
+        return singleSourceShortestPath(source, targets, numDests, false);
+    }
+
+    protected SourceData getSourceData(V source)
+    {
+        SourceData sd = sourceMap.get(source);
+        if (sd == null)
+            sd = new SourceData(source);
+        return sd;
+    }
+    
+    /**
+     * Returns the set of edges incident to <code>v</code> that should be tested.
+     * By default, this is the set of outgoing edges for instances of <code>Graph</code>,
+     * the set of incident edges for instances of <code>Hypergraph</code>,
+     * and is otherwise undefined.
+     */
+    protected Collection<E> getEdgesToCheck(V v)
+    {
+        if (g instanceof Graph)
+            return ((Graph<V,E>)g).getOutEdges(v);
+        else
+            return g.getIncidentEdges(v);
+
+    }
+
+    
+    /**
+     * Returns the length of a shortest path from the source to the target vertex,
+     * or null if the target is not reachable from the source.
+     * If either vertex is not in the graph for which this instance
+     * was created, throws <code>IllegalArgumentException</code>.
+     * 
+     * @see #getDistanceMap(Object)
+     * @see #getDistanceMap(Object,int)
+     */
+    public Number getDistance(V source, V target)
+    {
+        if (g.containsVertex(target) == false)
+            throw new IllegalArgumentException("Specified target vertex " + 
+                    target + " is not part of graph " + g);
+        if (g.containsVertex(source) == false)
+            throw new IllegalArgumentException("Specified source vertex " + 
+                    source + " is not part of graph " + g);
+        
+        Set<V> targets = new HashSet<V>();
+        targets.add(target);
+        Map<V,Number> distanceMap = getDistanceMap(source, targets);
+        return distanceMap.get(target);
+    }
+    
+
+    /**
+     * Returns a {@code Map} from each element {@code t} of {@code targets} to the 
+     * shortest-path distance from {@code source} to {@code t}. 
+     */
+    public Map<V,Number> getDistanceMap(V source, Collection<V> targets)
+    {
+       if (g.containsVertex(source) == false)
+            throw new IllegalArgumentException("Specified source vertex " + 
+                    source + " is not part of graph " + g);
+       if (targets.size() > max_targets)
+            throw new IllegalArgumentException("size of target set exceeds maximum " +
+                    "number of targets allowed: " + this.max_targets);
+        
+        Map<V,Number> distanceMap = 
+               singleSourceShortestPath(source, targets, 
+                               Math.min(g.getVertexCount(), max_targets));
+        if (!cached)
+            reset(source);
+        
+        return distanceMap;
+    }
+    
+    /**
+     * <p>Returns a <code>LinkedHashMap</code> which maps each vertex 
+     * in the graph (including the <code>source</code> vertex) 
+     * to its distance from the <code>source</code> vertex.
+     * The map's iterator will return the elements in order of 
+     * increasing distance from <code>source</code>.</p>
+     * 
+     * <p>The size of the map returned will be the number of 
+     * vertices reachable from <code>source</code>.</p>
+     * 
+     * @see #getDistanceMap(Object,int)
+     * @see #getDistance(Object,Object)
+     * @param source    the vertex from which distances are measured
+     */
+    public Map<V,Number> getDistanceMap(V source)
+    {
+        return getDistanceMap(source, Math.min(g.getVertexCount(), max_targets));
+    }
+    
+
+
+    /**
+     * <p>Returns a <code>LinkedHashMap</code> which maps each of the closest 
+     * <code>numDist</code> vertices to the <code>source</code> vertex 
+     * in the graph (including the <code>source</code> vertex) 
+     * to its distance from the <code>source</code> vertex.  Throws 
+     * an <code>IllegalArgumentException</code> if <code>source</code>
+     * is not in this instance's graph, or if <code>numDests</code> is 
+     * either less than 1 or greater than the number of vertices in the 
+     * graph.</p>
+     * 
+     * <p>The size of the map returned will be the smaller of 
+     * <code>numDests</code> and the number of vertices reachable from
+     * <code>source</code>. 
+     * 
+     * @see #getDistanceMap(Object)
+     * @see #getDistance(Object,Object)
+     * @param source    the vertex from which distances are measured
+     * @param numDests  the number of vertices for which to measure distances
+     */
+    public LinkedHashMap<V,Number> getDistanceMap(V source, int numDests)
+    {
+
+       if(g.getVertices().contains(source) == false) {
+            throw new IllegalArgumentException("Specified source vertex " + 
+                    source + " is not part of graph " + g);
+               
+       }
+        if (numDests < 1 || numDests > g.getVertexCount())
+            throw new IllegalArgumentException("numDests must be >= 1 " + 
+                "and <= g.numVertices()");
+
+        if (numDests > max_targets)
+            throw new IllegalArgumentException("numDests must be <= the maximum " +
+                    "number of targets allowed: " + this.max_targets);
+            
+        LinkedHashMap<V,Number> distanceMap = 
+               singleSourceShortestPath(source, null, numDests);
+                
+        if (!cached)
+            reset(source);
+        
+        return distanceMap;        
+    }
+    
+    /**
+     * Allows the user to specify the maximum distance that this instance will calculate.
+     * Any vertices past this distance will effectively be unreachable from the source, in
+     * the sense that the algorithm will not calculate the distance to any vertices which
+     * are farther away than this distance.  A negative value for <code>max_dist</code> 
+     * will ensure that no further distances are calculated.
+     * 
+     * <p>This can be useful for limiting the amount of time and space used by this algorithm
+     * if the graph is very large.</p>
+     * 
+     * <p>Note: if this instance has already calculated distances greater than <code>max_dist</code>,
+     * and the results are cached, those results will still be valid and available; this limit
+     * applies only to subsequent distance calculations.</p>
+     * @see #setMaxTargets(int)
+     */
+    public void setMaxDistance(double max_dist)
+    {
+        this.max_distance = max_dist;
+        for (V v : sourceMap.keySet())
+        {
+            SourceData sd = sourceMap.get(v);
+            sd.reached_max = (this.max_distance <= sd.dist_reached) || (sd.distances.size() >= max_targets);
+        }
+    }
+       
+    /**
+     * Allows the user to specify the maximum number of target vertices per source vertex 
+     * for which this instance will calculate distances.  Once this threshold is reached, 
+     * any further vertices will effectively be unreachable from the source, in
+     * the sense that the algorithm will not calculate the distance to any more vertices.  
+     * A negative value for <code>max_targets</code> will ensure that no further distances are calculated.
+     * 
+     * <p>This can be useful for limiting the amount of time and space used by this algorithm
+     * if the graph is very large.</p>
+     * 
+     * <p>Note: if this instance has already calculated distances to a greater number of 
+     * targets than <code>max_targets</code>, and the results are cached, those results 
+     * will still be valid and available; this limit applies only to subsequent distance 
+     * calculations.</p>
+     * @see #setMaxDistance(double)
+     */
+    public void setMaxTargets(int max_targets)
+    {
+        this.max_targets = max_targets;
+        for (V v : sourceMap.keySet())
+        {
+            SourceData sd = sourceMap.get(v);
+            sd.reached_max = (this.max_distance <= sd.dist_reached) || (sd.distances.size() >= max_targets);
+        }
+    }
+    
+    /**
+     * Clears all stored distances for this instance.  
+     * Should be called whenever the graph is modified (edge weights 
+     * changed or edges added/removed).  If the user knows that
+     * some currently calculated distances are unaffected by a
+     * change, <code>reset(V)</code> may be appropriate instead.
+     * 
+     * @see #reset(Object)
+     */
+    public void reset()
+    {
+        sourceMap = new HashMap<V,SourceData>();
+    }
+        
+    /**
+     * Specifies whether or not this instance of <code>DijkstraShortestPath</code>
+     * should cache its results (final and partial) for future reference.
+     * 
+     * @param enable    <code>true</code> if the results are to be cached, and
+     *                  <code>false</code> otherwise
+     */
+    public void enableCaching(boolean enable)
+    {
+        this.cached = enable;
+    }
+    
+    /**
+     * Clears all stored distances for the specified source vertex 
+     * <code>source</code>.  Should be called whenever the stored distances
+     * from this vertex are invalidated by changes to the graph.
+     * 
+     * @see #reset()
+     */
+    public void reset(V source)
+    {
+        sourceMap.put(source, null);
+    }
+
+    /**
+     * Compares according to distances, so that the BinaryHeap knows how to 
+     * order the tree.  
+     */
+    protected static class VertexComparator<V> implements Comparator<V>
+    {
+        private Map<V,Number> distances;
+        
+        protected VertexComparator(Map<V,Number> distances)
+        {
+            this.distances = distances;
+        }
+
+        public int compare(V o1, V o2)
+        {
+            return ((Double) distances.get(o1)).compareTo((Double) distances.get(o2));
+        }
+    }
+    
+    /**
+     * For a given source vertex, holds the estimated and final distances, 
+     * tentative and final assignments of incoming edges on the shortest path from
+     * the source vertex, and a priority queue (ordered by estimated distance)
+     * of the vertices for which distances are unknown.
+     * 
+     * @author Joshua O'Madadhain
+     */
+    protected class SourceData
+    {
+        protected LinkedHashMap<V,Number> distances;
+        protected Map<V,Number> estimatedDistances;
+        protected MapBinaryHeap<V> unknownVertices;
+        protected boolean reached_max = false;
+        protected double dist_reached = 0;
+
+        protected SourceData(V source)
+        {
+            distances = new LinkedHashMap<V,Number>();
+            estimatedDistances = new HashMap<V,Number>();
+            unknownVertices = new MapBinaryHeap<V>(new VertexComparator<V>(estimatedDistances));
+            
+            sourceMap.put(source, this);
+            
+            // initialize priority queue
+            estimatedDistances.put(source, new Double(0)); // distance from source to itself is 0
+            unknownVertices.add(source);
+            reached_max = false;
+            dist_reached = 0;
+        }
+        
+        protected Map.Entry<V,Number> getNextVertex()
+        {
+            V v = unknownVertices.remove();
+            Double dist = (Double)estimatedDistances.remove(v);
+            distances.put(v, dist);
+            return new BasicMapEntry<V,Number>(v, dist);
+        }
+        
+        protected void update(V dest, E tentative_edge, double new_dist)
+        {
+            estimatedDistances.put(dest, new_dist);
+            unknownVertices.update(dest);
+        }
+        
+        protected void createRecord(V w, E e, double new_dist)
+        {
+            estimatedDistances.put(w, new_dist);
+            unknownVertices.add(w);
+        }
+        
+        protected void restoreVertex(V v, double dist) 
+        {
+            estimatedDistances.put(v, dist);
+            unknownVertices.add(v);
+            distances.remove(v);
+        }
+    }
+}