Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a method to cap the size of the cache in DijkstraShortestPaths and DijkstraDistance #80

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ public class DijkstraDistance<V,E> implements Distance<V>
{
protected Hypergraph<V,E> g;
protected Function<? super E,? extends Number> nev;
protected Map<V,SourceData> sourceMap; // a map of source vertices to an instance of SourceData
protected LinkedHashMap<V,SourceData> sourceMap; // a map of source vertices to an instance of SourceData
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I missed a discussion of this, but is the cost (of a possible virtual lookup when declaring as the most general type) really a factor with modern JVMs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is a factor on modern JVMs, I've personally not heard of it. :/

IIRC, it's more idiomatic to keep the base type here as Map, i.e. whatever interface is most generic, or as high up the type hierarchy as possible, that has all the methods one needs.

Just my 2 cents.

protected boolean cached;
protected double max_distance;
protected int max_targets;
protected int max_cache_size;

/**
* <p>Creates an instance of <code>DijkstraShortestPath</code> for
Expand All @@ -80,11 +81,32 @@ public class DijkstraDistance<V,E> implements Distance<V>
* @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<V,E> g, Function<? super E,? extends Number> nev, boolean cached, int max_cache_size) {
this.g = g;
this.nev = nev;
this.sourceMap = new LinkedHashMap<V,SourceData>();
this.cached = cached;
this.max_cache_size = max_cache_size;
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 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, Function<? super E,? extends Number> nev, boolean cached) {
this.g = g;
this.nev = nev;
this.sourceMap = new HashMap<V,SourceData>();
this.sourceMap = new LinkedHashMap<V,SourceData>();
this.cached = cached;
this.max_distance = Double.POSITIVE_INFINITY;
this.max_targets = Integer.MAX_VALUE;
Expand All @@ -99,7 +121,7 @@ public DijkstraDistance(Hypergraph<V,E> g, Function<? super E,? extends Number>
* @param nev the class responsible for returning weights for edges
*/
public DijkstraDistance(Hypergraph<V,E> g, Function<? super E,? extends Number> nev) {
this(g, nev, true);
this(g, nev, true, Integer.MAX_VALUE);
}

/**
Expand All @@ -110,7 +132,7 @@ public DijkstraDistance(Hypergraph<V,E> g, Function<? super E,? extends Number>
* @param g the graph on which distances will be calculated
*/
public DijkstraDistance(Graph<V,E> g) {
this(g, Functions.constant(1), true);
this(g, Functions.constant(1), true, Integer.MAX_VALUE);
}

/**
Expand All @@ -120,9 +142,22 @@ public DijkstraDistance(Graph<V,E> 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<V,E> g, boolean cached, int max_cache_size) {
this(g, Functions.constant(1), cached, max_cache_size);
}

/**
* <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
*/
public DijkstraDistance(Graph<V,E> g, boolean cached) {
this(g, Functions.constant(1), cached);
this(g, Functions.constant(1), cached, Integer.MAX_VALUE);
}

/**
Expand Down Expand Up @@ -294,6 +329,9 @@ public Map<V,Number> getDistanceMap(V source, Collection<V> targets)
Math.min(g.getVertexCount(), max_targets));
if (!cached)
reset(source);

if (cached)
resizeCache();

return distanceMap;
}
Expand Down Expand Up @@ -363,6 +401,9 @@ public LinkedHashMap<V,Number> getDistanceMap(V source, int numDests)

if (!cached)
reset(source);

if (cached)
resizeCache();

return distanceMap;
}
Expand Down Expand Up @@ -436,7 +477,7 @@ public void setMaxTargets(int max_targets)
*/
public void reset()
{
sourceMap = new HashMap<V,SourceData>();
sourceMap = new LinkedHashMap<V,SourceData>();
}

/**
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ public class DijkstraShortestPath<V,E> extends DijkstraDistance<V,E> 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<V,E> g, Function<E, ? extends Number> nev, boolean cached, int max_cache_size)
{
super(g, nev, cached, max_cache_size);
}

/**
* <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 DijkstraShortestPath(Graph<V,E> g, Function<E, ? extends Number> nev, boolean cached)
{
Expand Down Expand Up @@ -89,7 +105,21 @@ public DijkstraShortestPath(Graph<V,E> g, boolean cached)
{
super(g, cached);
}


/**
* <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
* @param max_cache_size the maximum number of from-to combinations cached, to control memory usage
*/
public DijkstraShortestPath(Graph<V,E> g, boolean cached,int max_cache_size)
{
super(g, cached, max_cache_size);
}

@Override
protected SourceData getSourceData(V source)
{
Expand Down Expand Up @@ -132,6 +162,9 @@ public E getIncomingEdge(V source, V target)

if (!cached)
reset(source);

if (cached)
resizeCache();

return incomingEdge;
}
Expand Down Expand Up @@ -196,7 +229,14 @@ public List<E> getPath(V source, V target)
path.addFirst(incoming);
current = ((Graph<V,E>)g).getOpposite(current, incoming);
}
return path;

if (!cached)
reset(source);

if (cached)
resizeCache();

return path;
}


Expand Down Expand Up @@ -235,6 +275,9 @@ public LinkedHashMap<V,E> getIncomingEdgeMap(V source, int numDests)

if (!cached)
reset(source);

if (cached)
resizeCache();

return incomingEdgeMap;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String,Integer> ug;
List<String> vertices;

public void testAllPathsUnweighted(){

setUp();

DijkstraShortestPath<String,Integer> sp = new DijkstraShortestPath<String, Integer>(ug,true, 100);

Map<String,Map<String,Number>> allSp = new HashMap<String, Map<String, Number>>();

// 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<Integer> 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<String,Integer> sp = new DijkstraShortestPath<String, Integer>(ug, true, 100);

Map<String,Map<String,Number>> allSp = new HashMap<String, Map<String, Number>>();

// Find shortest path distance from every vertex to every vertex
for (int v1 = 0; v1 < vertices.size(); v1++) {
Map<String, Number> 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<String,Integer>();
vertices = new ArrayList<String>();

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++;

}

}

}
}