diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraDistance.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraDistance.java index 01509d22..f6d623f9 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraDistance.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraDistance.java @@ -66,10 +66,11 @@ public class DijkstraDistance implements Distance { protected Hypergraph g; protected Function nev; - protected Map sourceMap; // a map of source vertices to an instance of SourceData + protected LinkedHashMap sourceMap; // a map of source vertices to an instance of SourceData protected boolean cached; protected double max_distance; protected int max_targets; + protected int max_cache_size; /** *

Creates an instance of DijkstraShortestPath for @@ -80,11 +81,32 @@ public class DijkstraDistance implements Distance * @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 + * @param max_cache_size the maximum number of from-to combinations cached, to control memory usage + */ + public DijkstraDistance(Hypergraph g, Function nev, boolean cached, int max_cache_size) { + this.g = g; + this.nev = nev; + this.sourceMap = new LinkedHashMap(); + this.cached = cached; + this.max_cache_size = max_cache_size; + this.max_distance = Double.POSITIVE_INFINITY; + this.max_targets = Integer.MAX_VALUE; + } + + /** + *

Creates an instance of DijkstraShortestPath for + * the specified graph and the specified method of extracting weights + * from edges, which caches results locally if and only if + * cached is true. + * + * @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 g, Function nev, boolean cached) { this.g = g; this.nev = nev; - this.sourceMap = new HashMap(); + this.sourceMap = new LinkedHashMap(); this.cached = cached; this.max_distance = Double.POSITIVE_INFINITY; this.max_targets = Integer.MAX_VALUE; @@ -99,7 +121,7 @@ public DijkstraDistance(Hypergraph g, Function * @param nev the class responsible for returning weights for edges */ public DijkstraDistance(Hypergraph g, Function nev) { - this(g, nev, true); + this(g, nev, true, Integer.MAX_VALUE); } /** @@ -110,7 +132,7 @@ public DijkstraDistance(Hypergraph g, Function * @param g the graph on which distances will be calculated */ public DijkstraDistance(Graph g) { - this(g, Functions.constant(1), true); + this(g, Functions.constant(1), true, Integer.MAX_VALUE); } /** @@ -120,9 +142,22 @@ public DijkstraDistance(Graph g) { * * @param g the graph on which distances will be calculated * @param cached specifies whether the results are to be cached + * @param max_cache_size the maximum number of from-to combinations cached, to control memory usage */ + public DijkstraDistance(Graph g, boolean cached, int max_cache_size) { + this(g, Functions.constant(1), cached, max_cache_size); + } + + /** + *

Creates an instance of DijkstraShortestPath 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 + */ public DijkstraDistance(Graph g, boolean cached) { - this(g, Functions.constant(1), cached); + this(g, Functions.constant(1), cached, Integer.MAX_VALUE); } /** @@ -294,6 +329,9 @@ public Map getDistanceMap(V source, Collection targets) Math.min(g.getVertexCount(), max_targets)); if (!cached) reset(source); + + if (cached) + resizeCache(); return distanceMap; } @@ -363,6 +401,9 @@ public LinkedHashMap getDistanceMap(V source, int numDests) if (!cached) reset(source); + + if (cached) + resizeCache(); return distanceMap; } @@ -436,7 +477,7 @@ public void setMaxTargets(int max_targets) */ public void reset() { - sourceMap = new HashMap(); + sourceMap = new LinkedHashMap(); } /** @@ -465,6 +506,19 @@ public void reset(V source) sourceMap.put(source, null); } + /** + * + * COMMENTS + * + */ + public void resizeCache(){ + + if (sourceMap.size() > max_cache_size){ + sourceMap.remove(sourceMap.keySet().iterator().next()); // remove the first FROM - TO combination in the cache + } + + } + /** * Compares according to distances, so that the BinaryHeap knows how to * order the tree. diff --git a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraShortestPath.java b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraShortestPath.java index a3c61da0..8c3f1b6a 100644 --- a/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraShortestPath.java +++ b/jung-algorithms/src/main/java/edu/uci/ics/jung/algorithms/shortestpath/DijkstraShortestPath.java @@ -46,6 +46,22 @@ public class DijkstraShortestPath extends DijkstraDistance implements * @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 + * @param max_cache_size the maximum number of from-to combinations cached, to control memory usage + */ + public DijkstraShortestPath(Graph g, Function nev, boolean cached, int max_cache_size) + { + super(g, nev, cached, max_cache_size); + } + + /** + *

Creates an instance of DijkstraShortestPath for + * the specified graph and the specified method of extracting weights + * from edges, which caches results locally if and only if + * cached is true. + * + * @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 DijkstraShortestPath(Graph g, Function nev, boolean cached) { @@ -89,7 +105,21 @@ public DijkstraShortestPath(Graph g, boolean cached) { super(g, cached); } - + + /** + *

Creates an instance of DijkstraShortestPath 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 + * @param max_cache_size the maximum number of from-to combinations cached, to control memory usage + */ + public DijkstraShortestPath(Graph g, boolean cached,int max_cache_size) + { + super(g, cached, max_cache_size); + } + @Override protected SourceData getSourceData(V source) { @@ -132,6 +162,9 @@ public E getIncomingEdge(V source, V target) if (!cached) reset(source); + + if (cached) + resizeCache(); return incomingEdge; } @@ -196,7 +229,14 @@ public List getPath(V source, V target) path.addFirst(incoming); current = ((Graph)g).getOpposite(current, incoming); } - return path; + + if (!cached) + reset(source); + + if (cached) + resizeCache(); + + return path; } @@ -235,6 +275,9 @@ public LinkedHashMap getIncomingEdgeMap(V source, int numDests) if (!cached) reset(source); + + if (cached) + resizeCache(); return incomingEdgeMap; } diff --git a/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestCachingLargeGraph.java b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestCachingLargeGraph.java new file mode 100644 index 00000000..be5499dd --- /dev/null +++ b/jung-algorithms/src/test/java/edu/uci/ics/jung/algorithms/shortestpath/TestCachingLargeGraph.java @@ -0,0 +1,106 @@ +package edu.uci.ics.jung.algorithms.shortestpath; + +import edu.uci.ics.jung.graph.Graph; +import edu.uci.ics.jung.graph.UndirectedGraph; +import edu.uci.ics.jung.graph.UndirectedSparseGraph; +import edu.uci.ics.jung.graph.util.EdgeType; +import junit.framework.TestCase; +import junit.framework.TestResult; +import org.junit.Ignore; + +import java.util.*; + +/** + * Created by Lewis Heptonstall on 06/05/2017. + * + * Tests demonstrate the value of capping the max_cache_size when doing many, recurrent shortest paths, calculations + * All tests fail when max_cache_size is not set with either OutOfMemory or GC Limit Exceeded. + * All test should pass when max_cache_size set to 100 + * Please set int vertex_count = 4000. + * VM options used: -Xms128m -Xmx750m + */ +public class TestCachingLargeGraph extends TestCase { + + Graph ug; + List vertices; + + public void testAllPathsUnweighted(){ + + setUp(); + + DijkstraShortestPath sp = new DijkstraShortestPath(ug,true, 100); + + Map> allSp = new HashMap>(); + + // Find shortest path from every vertex to every vertex + for (int v1 = 0; v1 < vertices.size(); v1++) { + + for (int v2 = 0; v2 < vertices.size(); v2 ++) { + List thisPath = sp.getPath(vertices.get(v1),vertices.get(v2)); + } + + if (v1 % 100 == 0){ // Every 100 iterations, print progress + System.out.println("Progress: " + new Double(v1)/vertices.size()); + } + + } + + } + + public void testDistanceMapUnweighted(){ + + setUp(); + + DijkstraShortestPath sp = new DijkstraShortestPath(ug, true, 100); + + Map> allSp = new HashMap>(); + + // Find shortest path distance from every vertex to every vertex + for (int v1 = 0; v1 < vertices.size(); v1++) { + Map test = sp.getDistanceMap(vertices.get(v1)); + + allSp.put(vertices.get(v1),test); + + if (v1 % 100 == 0){ // Every 100 iterations, print progress + System.out.println("Progress: " + new Double(v1)/vertices.size()); + } + + } + + } + + @Override + protected void setUp() { + + /* + Builds a very large, sparsely connected, unweighted, undirected graph + */ + + ug = new UndirectedSparseGraph(); + vertices = new ArrayList(); + + int vertex_count = 40; + int max_connections_per_vertex = 30; + Random random = new Random(); + random.setSeed(1234); + + // Add vertex_count number of vertices to the list, with a random ID + for (int v = 0; v < vertex_count; v++) { + vertices.add(((Long) random.nextLong()).toString()); + ug.addVertex(vertices.get(v)); + } + + int e = 0; // edge iterator + + for (int v1 = 0; v1 < vertices.size(); v1++) { + for (int v2 = v1 + 1; v2 < ((v1 + max_connections_per_vertex < vertices.size()) ? (v1 + max_connections_per_vertex) : vertices.size()); v2++){ + + ug.addEdge(e,vertices.get(v1),vertices.get(v2), EdgeType.UNDIRECTED); + e++; + + } + + } + + } +}