ONOS-745 Refactoring topology to compute only broadcast tree and not pre-compute paths.
ONOS-744 Refactoring graph search to allow requesting max number of paths.
Change-Id: I28467246b92df32ebb3155c45774ecc051fdd3dd
diff --git a/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java b/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java
index 6541ad3..9554be1 100644
--- a/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/AbstractGraphPathSearch.java
@@ -65,17 +65,31 @@
protected final Set<Path<V, E>> paths = new HashSet<>();
protected final Map<V, Double> costs = new HashMap<>();
protected final Map<V, Set<E>> parents = new HashMap<>();
+ protected final int maxPaths;
/**
- * Creates the result of path search.
+ * Creates the result of a single-path search.
*
* @param src path source
* @param dst optional path destination
*/
public DefaultResult(V src, V dst) {
+ this(src, dst, 1);
+ }
+
+ /**
+ * Creates the result of path search.
+ *
+ * @param src path source
+ * @param dst optional path destination
+ * @param maxPaths optional limit of number of paths;
+ * {@link GraphPathSearch#ALL_PATHS} if no limit
+ */
+ public DefaultResult(V src, V dst, int maxPaths) {
checkNotNull(src, "Source cannot be null");
this.src = src;
this.dst = dst;
+ this.maxPaths = maxPaths;
}
@Override
@@ -126,7 +140,8 @@
/**
* Updates the cost of the vertex using its existing cost plus the
- * cost to traverse the specified edge.
+ * cost to traverse the specified edge. If the search is in single
+ * path mode, only one path will be accrued.
*
* @param vertex vertex to update
* @param edge edge through which vertex is reached
@@ -147,7 +162,9 @@
if (replace) {
edges.clear();
}
- edges.add(edge);
+ if (maxPaths == ALL_PATHS || edges.size() < maxPaths) {
+ edges.add(edge);
+ }
}
}
@@ -203,7 +220,7 @@
for (V v : destinations) {
// Ignore the source, if it is among the destinations.
if (!v.equals(src)) {
- buildAllPaths(this, src, v);
+ buildAllPaths(this, src, v, maxPaths);
}
}
}
@@ -215,18 +232,21 @@
* graph search result by applying breadth-first search through the parent
* edges and vertex costs.
*
- * @param result graph search result
- * @param src source vertex
- * @param dst destination vertex
+ * @param result graph search result
+ * @param src source vertex
+ * @param dst destination vertex
+ * @param maxPaths limit on the number of paths built;
+ * {@link GraphPathSearch#ALL_PATHS} if no limit
*/
- private void buildAllPaths(DefaultResult result, V src, V dst) {
+ private void buildAllPaths(DefaultResult result, V src, V dst, int maxPaths) {
DefaultMutablePath<V, E> basePath = new DefaultMutablePath<>();
basePath.setCost(result.cost(dst));
Set<DefaultMutablePath<V, E>> pendingPaths = new HashSet<>();
pendingPaths.add(basePath);
- while (!pendingPaths.isEmpty()) {
+ while (!pendingPaths.isEmpty() &&
+ (maxPaths == ALL_PATHS || result.paths.size() < maxPaths)) {
Set<DefaultMutablePath<V, E>> frontier = new HashSet<>();
for (DefaultMutablePath<V, E> path : pendingPaths) {
diff --git a/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java b/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java
index caf8019..39e39e8 100644
--- a/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/BellmanFordGraphSearch.java
@@ -24,11 +24,11 @@
@Override
public Result<V, E> search(Graph<V, E> graph, V src, V dst,
- EdgeWeight<V, E> weight) {
+ EdgeWeight<V, E> weight, int maxPaths) {
checkArguments(graph, src, dst);
// Prepare the graph search result.
- DefaultResult result = new DefaultResult(src, dst);
+ DefaultResult result = new DefaultResult(src, dst, maxPaths);
// The source vertex has cost 0, of course.
result.updateVertex(src, null, 0.0, true);
diff --git a/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java b/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java
index f5c60db..588b0fe 100644
--- a/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/BreadthFirstSearch.java
@@ -26,11 +26,11 @@
@Override
public Result<V, E> search(Graph<V, E> graph, V src, V dst,
- EdgeWeight<V, E> weight) {
+ EdgeWeight<V, E> weight, int maxPaths) {
checkArguments(graph, src, dst);
// Prepare the graph result.
- DefaultResult result = new DefaultResult(src, dst);
+ DefaultResult result = new DefaultResult(src, dst, maxPaths);
// Setup the starting frontier with the source as the sole vertex.
Set<V> frontier = new HashSet<>();
diff --git a/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java b/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java
index e61a9c1..f839fff 100644
--- a/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/DepthFirstSearch.java
@@ -36,11 +36,11 @@
@Override
public SpanningTreeResult search(Graph<V, E> graph, V src, V dst,
- EdgeWeight<V, E> weight) {
+ EdgeWeight<V, E> weight, int maxPaths) {
checkArguments(graph, src, dst);
// Prepare the search result.
- SpanningTreeResult result = new SpanningTreeResult(src, dst);
+ SpanningTreeResult result = new SpanningTreeResult(src, dst, maxPaths);
// The source vertex has cost 0, of course.
result.updateVertex(src, null, 0.0, true);
@@ -141,11 +141,12 @@
/**
* Creates a new spanning tree result.
*
- * @param src search source
- * @param dst optional search destination
+ * @param src search source
+ * @param dst optional search destination
+ * @param maxPaths limit on the number of paths
*/
- public SpanningTreeResult(V src, V dst) {
- super(src, dst);
+ public SpanningTreeResult(V src, V dst, int maxPaths) {
+ super(src, dst, maxPaths);
}
/**
diff --git a/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java b/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java
index eada84c..53d211a 100644
--- a/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/DijkstraGraphSearch.java
@@ -28,12 +28,12 @@
@Override
public Result<V, E> search(Graph<V, E> graph, V src, V dst,
- EdgeWeight<V, E> weight) {
+ EdgeWeight<V, E> weight, int maxPaths) {
checkArguments(graph, src, dst);
// Use the default result to remember cumulative costs and parent
// edges to each each respective vertex.
- DefaultResult result = new DefaultResult(src, dst);
+ DefaultResult result = new DefaultResult(src, dst, maxPaths);
// Cost to reach the source vertex is 0 of course.
result.updateVertex(src, null, 0.0, false);
diff --git a/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java b/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java
index b838421..f869a02 100644
--- a/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/GraphPathSearch.java
@@ -26,6 +26,8 @@
*/
public interface GraphPathSearch<V extends Vertex, E extends Edge<V>> {
+ public static int ALL_PATHS = -1;
+
/**
* Abstraction of a path search result.
*/
@@ -68,16 +70,18 @@
}
/**
- * Searches the specified graph.
+ * Searches the specified graph for paths between vertices.
*
- * @param graph graph to be searched
- * @param src optional source vertex
- * @param dst optional destination vertex; if null paths to all vertex
- * destinations will be searched
- * @param weight optional edge-weight; if null cost of each edge will be
- * assumed to be 1.0
+ * @param graph graph to be searched
+ * @param src optional source vertex
+ * @param dst optional destination vertex; if null paths to all vertex
+ * destinations will be searched
+ * @param weight optional edge-weight; if null cost of each edge will be
+ * assumed to be 1.0
+ * @param maxPaths limit on number of paths; {@link GraphPathSearch#ALL_PATHS} if no limit
* @return search results
*/
- Result<V, E> search(Graph<V, E> graph, V src, V dst, EdgeWeight<V, E> weight);
+ Result<V, E> search(Graph<V, E> graph, V src, V dst,
+ EdgeWeight<V, E> weight, int maxPaths);
}
diff --git a/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java b/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java
index 86f29ef..db8402d 100644
--- a/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java
+++ b/utils/misc/src/main/java/org/onlab/graph/KshortestPathSearch.java
@@ -23,6 +23,8 @@
//import java.util.PriorityQueue;
import java.util.Set;
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
+
/**
* K-shortest-path graph search algorithm capable of finding not just one,
* but K shortest paths with ascending order between the source and destinations.
@@ -137,7 +139,7 @@
private List<E> searchShortestPath(Graph<V, E> graph, V src, V dst) {
// Determine the shortest path from the source to the destination by using the Dijkstra algorithm.
DijkstraGraphSearch dijkstraAlg = new DijkstraGraphSearch();
- Set<Path> paths = dijkstraAlg.search(graph, src, dst, weight).paths();
+ Set<Path> paths = dijkstraAlg.search(graph, src, dst, weight, ALL_PATHS).paths();
Iterator<Path> itr = paths.iterator();
if (!itr.hasNext()) {
return null;
diff --git a/utils/misc/src/test/java/org/onlab/graph/AbstractGraphPathSearchTest.java b/utils/misc/src/test/java/org/onlab/graph/AbstractGraphPathSearchTest.java
index cf35f66..91204fc 100644
--- a/utils/misc/src/test/java/org/onlab/graph/AbstractGraphPathSearchTest.java
+++ b/utils/misc/src/test/java/org/onlab/graph/AbstractGraphPathSearchTest.java
@@ -36,19 +36,19 @@
public void noSuchSourceArgument() {
graphSearch().search(new AdjacencyListsGraph<>(of(B, C),
of(new TestEdge(B, C, 1))),
- A, H, weight);
+ A, H, weight, 1);
}
@Test(expected = NullPointerException.class)
public void nullGraphArgument() {
- graphSearch().search(null, A, H, weight);
+ graphSearch().search(null, A, H, weight, 1);
}
@Test(expected = NullPointerException.class)
public void nullSourceArgument() {
graphSearch().search(new AdjacencyListsGraph<>(of(B, C),
of(new TestEdge(B, C, 1))),
- null, H, weight);
+ null, H, weight, 1);
}
@Test
diff --git a/utils/misc/src/test/java/org/onlab/graph/BellmanFordGraphSearchTest.java b/utils/misc/src/test/java/org/onlab/graph/BellmanFordGraphSearchTest.java
index 4e9f5e6..9380054 100644
--- a/utils/misc/src/test/java/org/onlab/graph/BellmanFordGraphSearchTest.java
+++ b/utils/misc/src/test/java/org/onlab/graph/BellmanFordGraphSearchTest.java
@@ -21,6 +21,7 @@
import java.util.Set;
import static org.junit.Assert.assertEquals;
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
/**
* Test of the Bellman-Ford algorithm.
@@ -56,7 +57,7 @@
graph = new AdjacencyListsGraph<>(vertexes, edges);
GraphPathSearch<TestVertex, TestEdge> search = graphSearch();
- Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight).paths();
+ Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight, ALL_PATHS).paths();
assertEquals("incorrect paths count", 1, paths.size());
Path p = paths.iterator().next();
@@ -65,10 +66,10 @@
assertEquals("incorrect path length", 5, p.edges().size());
assertEquals("incorrect path cost", 5.0, p.cost(), 0.1);
- paths = search.search(graph, A, G, weight).paths();
+ paths = search.search(graph, A, G, weight, ALL_PATHS).paths();
assertEquals("incorrect paths count", 0, paths.size());
- paths = search.search(graph, A, null, weight).paths();
+ paths = search.search(graph, A, null, weight, ALL_PATHS).paths();
printPaths(paths);
assertEquals("incorrect paths count", 6, paths.size());
}
diff --git a/utils/misc/src/test/java/org/onlab/graph/BreadthFirstSearchTest.java b/utils/misc/src/test/java/org/onlab/graph/BreadthFirstSearchTest.java
index 96a77e6..76c0cc2 100644
--- a/utils/misc/src/test/java/org/onlab/graph/BreadthFirstSearchTest.java
+++ b/utils/misc/src/test/java/org/onlab/graph/BreadthFirstSearchTest.java
@@ -20,6 +20,7 @@
import java.util.Set;
import static org.junit.Assert.assertEquals;
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
/**
* Test of the BFS and similar path search algorithms.
@@ -47,7 +48,8 @@
graph = new AdjacencyListsGraph<>(vertexes(), edges());
GraphPathSearch<TestVertex, TestEdge> search = graphSearch();
- Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, H, weight).paths();
+ Set<Path<TestVertex, TestEdge>> paths =
+ search.search(graph, A, H, weight, ALL_PATHS).paths();
assertEquals("incorrect paths count", 1, paths.size());
Path p = paths.iterator().next();
@@ -56,7 +58,7 @@
assertEquals("incorrect path length", pathLength, p.edges().size());
assertEquals("incorrect path cost", pathCost, p.cost(), 0.1);
- paths = search.search(graph, A, null, weight).paths();
+ paths = search.search(graph, A, null, weight, ALL_PATHS).paths();
printPaths(paths);
assertEquals("incorrect paths count", pathCount, paths.size());
}
@@ -68,7 +70,7 @@
EdgeWeight<TestVertex, TestEdge> weight,
int pathCount, double pathCost) {
GraphPathSearch.Result<TestVertex, TestEdge> result =
- search.search(graph, src, dst, weight);
+ search.search(graph, src, dst, weight, ALL_PATHS);
Set<Path<TestVertex, TestEdge>> paths = result.paths();
printPaths(paths);
assertEquals("incorrect paths count", pathCount, paths.size());
@@ -78,4 +80,21 @@
}
}
+ // Executes the single-path search and validates its results.
+ protected void executeSinglePathSearch(GraphPathSearch<TestVertex, TestEdge> search,
+ Graph<TestVertex, TestEdge> graph,
+ TestVertex src, TestVertex dst,
+ EdgeWeight<TestVertex, TestEdge> weight,
+ int pathCount, double pathCost) {
+ GraphPathSearch.Result<TestVertex, TestEdge> result =
+ search.search(graph, src, dst, weight, 1);
+ Set<Path<TestVertex, TestEdge>> paths = result.paths();
+ printPaths(paths);
+ assertEquals("incorrect paths count", Math.min(pathCount, 1), paths.size());
+ if (pathCount > 0) {
+ Path<TestVertex, TestEdge> path = paths.iterator().next();
+ assertEquals("incorrect path cost", pathCost, path.cost(), 0.1);
+ }
+ }
+
}
diff --git a/utils/misc/src/test/java/org/onlab/graph/DepthFirstSearchTest.java b/utils/misc/src/test/java/org/onlab/graph/DepthFirstSearchTest.java
index d225377..5348bf6 100644
--- a/utils/misc/src/test/java/org/onlab/graph/DepthFirstSearchTest.java
+++ b/utils/misc/src/test/java/org/onlab/graph/DepthFirstSearchTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.onlab.graph.DepthFirstSearch.EdgeType;
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
/**
* Test of the DFS algorithm.
@@ -52,7 +53,7 @@
DepthFirstSearch<TestVertex, TestEdge> search = graphSearch();
DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result =
- search.search(graph, A, H, weight);
+ search.search(graph, A, H, weight, 1);
Set<Path<TestVertex, TestEdge>> paths = result.paths();
assertEquals("incorrect path count", 1, paths.size());
@@ -77,7 +78,7 @@
// Perform narrow path search to a specific destination.
DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result =
- search.search(graph, A, null, weight);
+ search.search(graph, A, null, weight, ALL_PATHS);
assertEquals("incorrect paths count", 7, result.paths().size());
int[] types = new int[]{0, 0, 0, 0};
diff --git a/utils/misc/src/test/java/org/onlab/graph/DijkstraGraphSearchTest.java b/utils/misc/src/test/java/org/onlab/graph/DijkstraGraphSearchTest.java
index e11ccb2..d906e65 100644
--- a/utils/misc/src/test/java/org/onlab/graph/DijkstraGraphSearchTest.java
+++ b/utils/misc/src/test/java/org/onlab/graph/DijkstraGraphSearchTest.java
@@ -55,16 +55,16 @@
new TestEdge(C, D, 1),
new TestEdge(D, C, 1)));
GraphPathSearch<TestVertex, TestEdge> gs = graphSearch();
- Set<Path<TestVertex, TestEdge>> paths = gs.search(graph, A, B, weight).paths();
+ Set<Path<TestVertex, TestEdge>> paths = gs.search(graph, A, B, weight, 1).paths();
printPaths(paths);
assertEquals("incorrect paths count", 1, paths.size());
assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1);
- paths = gs.search(graph, A, D, weight).paths();
+ paths = gs.search(graph, A, D, weight, 1).paths();
printPaths(paths);
assertEquals("incorrect paths count", 0, paths.size());
- paths = gs.search(graph, A, null, weight).paths();
+ paths = gs.search(graph, A, null, weight, 1).paths();
printPaths(paths);
assertEquals("incorrect paths count", 1, paths.size());
assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1);
@@ -78,6 +78,7 @@
new TestEdge(B, D, 1),
new TestEdge(C, D, 1)));
executeSearch(graphSearch(), graph, A, D, weight, 2, 2.0);
+ executeSinglePathSearch(graphSearch(), graph, A, D, weight, 1, 2.0);
}
@Test
@@ -93,6 +94,7 @@
new TestEdge(F, G, 1),
new TestEdge(A, G, 4)));
executeSearch(graphSearch(), graph, A, G, weight, 5, 4.0);
+ executeSinglePathSearch(graphSearch(), graph, A, G, weight, 1, 4.0);
}
@Test
@@ -106,6 +108,7 @@
new TestEdge(F, G, 1), new TestEdge(F, H, 1),
new TestEdge(A, E, 3), new TestEdge(B, D, 1)));
executeSearch(graphSearch(), graph, A, E, weight, 3, 3.0);
+ executeSinglePathSearch(graphSearch(), graph, A, E, weight, 1, 3.0);
}
@Test
@@ -123,6 +126,7 @@
new TestEdge(G, A, -5),
new TestEdge(A, G, 4)));
executeSearch(graphSearch(), graph, A, G, weight, 3, 4.0);
+ executeSinglePathSearch(graphSearch(), graph, A, G, weight, 1, 4.0);
}
@Test