Committed by
Gerrit Code Review
Rebuilding K Paths algorithm
Change-Id: I8f5223cd3004ec89b203b1f177cd226911426906
Showing
4 changed files
with
338 additions
and
483 deletions
| 1 | +/* | ||
| 2 | + * Copyright 2015 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | +package org.onlab.graph; | ||
| 17 | + | ||
| 18 | +import com.google.common.collect.ImmutableSet; | ||
| 19 | +import com.google.common.collect.Lists; | ||
| 20 | +import com.google.common.collect.Sets; | ||
| 21 | +import org.slf4j.Logger; | ||
| 22 | + | ||
| 23 | +import java.util.ArrayList; | ||
| 24 | +import java.util.Comparator; | ||
| 25 | +import java.util.List; | ||
| 26 | +import java.util.Set; | ||
| 27 | +import java.util.TreeSet; | ||
| 28 | + | ||
| 29 | +import static com.google.common.base.Preconditions.checkArgument; | ||
| 30 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
| 31 | +import static org.slf4j.LoggerFactory.getLogger; | ||
| 32 | + | ||
| 33 | +/** | ||
| 34 | + * Runs K shortest paths algorithm on a provided directed graph. Returns results in the form of an | ||
| 35 | + * InnerOrderedResult so iteration through the returned paths will return paths in ascending order according to the | ||
| 36 | + * provided EdgeWeight. | ||
| 37 | + */ | ||
| 38 | +public class KShortestPathsSearch<V extends Vertex, E extends Edge<V>> extends AbstractGraphPathSearch<V, E> { | ||
| 39 | + | ||
| 40 | + private final Logger log = getLogger(getClass()); | ||
| 41 | + | ||
| 42 | + @Override | ||
| 43 | + public Result<V, E> search(Graph<V, E> graph, V src, V dst, EdgeWeight<V, E> weight, int maxPaths) { | ||
| 44 | + checkNotNull(src); | ||
| 45 | + checkNotNull(dst); | ||
| 46 | + //The modified edge weight removes any need to modify the original graph | ||
| 47 | + InnerEdgeWeighter modifiedWeighter = new InnerEdgeWeighter(checkNotNull(weight)); | ||
| 48 | + checkArgument(maxPaths > 0); | ||
| 49 | + Graph<V, E> originalGraph = checkNotNull(graph); | ||
| 50 | + //the result contains the set of eventual results | ||
| 51 | + InnerOrderedResult result = new InnerOrderedResult(src, dst, maxPaths); | ||
| 52 | + ArrayList<Path<V, E>> resultPaths = new ArrayList<>(maxPaths); | ||
| 53 | + ArrayList<Path<V, E>> potentialPaths = Lists.newArrayList(); | ||
| 54 | + | ||
| 55 | + DijkstraGraphSearch<V, E> dijkstraSearch = new DijkstraGraphSearch<>(); | ||
| 56 | + Set<Path<V, E>> dijkstraResults = dijkstraSearch.search(originalGraph, src, dst, modifiedWeighter, 1).paths(); | ||
| 57 | + //Checks if the dst was reachable | ||
| 58 | + if (dijkstraResults.size() == 0) { | ||
| 59 | + log.warn("No path was found."); | ||
| 60 | + return result; | ||
| 61 | + } | ||
| 62 | + //If it was reachable adds the first shortest path to the set of results | ||
| 63 | + resultPaths.add(dijkstraResults.iterator().next()); | ||
| 64 | + | ||
| 65 | + for (int k = 1; k < maxPaths; k++) { | ||
| 66 | + | ||
| 67 | + for (int i = 0; i < (resultPaths.get(k - 1).edges().size() - 1); i++) { | ||
| 68 | + V spurNode = resultPaths.get(k - 1).edges().get(i).src(); | ||
| 69 | + List<E> rootPathEdgeList = resultPaths.get(k - 1).edges().subList(0, i); | ||
| 70 | + | ||
| 71 | + for (Path<V, E> path : resultPaths) { | ||
| 72 | + if (edgeListsAreEqual(rootPathEdgeList, path.edges().subList(0, i))) { | ||
| 73 | + modifiedWeighter.removedEdges.add(path.edges().get(i)); | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + //Effectively remove all nodes from the source path | ||
| 78 | + for (E edge : rootPathEdgeList) { | ||
| 79 | + originalGraph.getEdgesFrom(edge.src()).forEach(e -> modifiedWeighter.removedEdges.add(e)); | ||
| 80 | + originalGraph.getEdgesTo(edge.src()).forEach(e -> modifiedWeighter.removedEdges.add(e)); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + dijkstraResults = dijkstraSearch.search(originalGraph, spurNode, dst, modifiedWeighter, 1).paths(); | ||
| 84 | + if (dijkstraResults.size() != 0) { | ||
| 85 | + Path<V, E> spurPath = dijkstraResults.iterator().next(); | ||
| 86 | + List<E> totalPath = new ArrayList<>(rootPathEdgeList); | ||
| 87 | + spurPath.edges().forEach(e -> totalPath.add(e)); | ||
| 88 | + //The following line must use the original weighter not the modified weighter because the modified | ||
| 89 | + //weighter will count -1 values used for modifying the graph and return an inaccurate cost. | ||
| 90 | + potentialPaths.add(new DefaultPath<V, E>(totalPath, | ||
| 91 | + calculatePathCost(weight, totalPath))); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + //Restore all removed paths and nodes | ||
| 95 | + modifiedWeighter.removedEdges.clear(); | ||
| 96 | + } | ||
| 97 | + if (potentialPaths.isEmpty()) { | ||
| 98 | + break; | ||
| 99 | + } | ||
| 100 | + potentialPaths.sort(new InnerPathComparator()); | ||
| 101 | + resultPaths.add(potentialPaths.get(0)); | ||
| 102 | + potentialPaths.remove(0); | ||
| 103 | + } | ||
| 104 | + result.pathSet.addAll(resultPaths); | ||
| 105 | + | ||
| 106 | + return result; | ||
| 107 | + } | ||
| 108 | + //Edge list equality is judges by shared endpoints, and shared endpoints should be the same | ||
| 109 | + private boolean edgeListsAreEqual(List<E> edgeListOne, List<E> edgeListTwo) { | ||
| 110 | + if (edgeListOne.size() != edgeListTwo.size()) { | ||
| 111 | + return false; | ||
| 112 | + } | ||
| 113 | + E edgeOne; | ||
| 114 | + E edgeTwo; | ||
| 115 | + for (int i = 0; i < edgeListOne.size(); i++) { | ||
| 116 | + edgeOne = edgeListOne.get(i); | ||
| 117 | + edgeTwo = edgeListTwo.get(i); | ||
| 118 | + if (!edgeOne.equals(edgeTwo)) { | ||
| 119 | + return false; | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + return true; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + private Double calculatePathCost(EdgeWeight<V, E> weighter, List<E> edges) { | ||
| 126 | + Double totalCost = 0.0; | ||
| 127 | + for (E edge : edges) { | ||
| 128 | + totalCost += weighter.weight(edge); | ||
| 129 | + } | ||
| 130 | + return totalCost; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + /** | ||
| 134 | + * Weights edges to make them inaccessible if set, otherwise returns the result of the original EdgeWeight. | ||
| 135 | + */ | ||
| 136 | + private class InnerEdgeWeighter implements EdgeWeight<V, E> { | ||
| 137 | + | ||
| 138 | + private Set<E> removedEdges = Sets.newConcurrentHashSet(); | ||
| 139 | + private EdgeWeight<V, E> innerEdgeWeight; | ||
| 140 | + | ||
| 141 | + public InnerEdgeWeighter(EdgeWeight<V, E> weight) { | ||
| 142 | + this.innerEdgeWeight = weight; | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + @Override | ||
| 146 | + public double weight(E edge) { | ||
| 147 | + if (removedEdges.contains(edge)) { | ||
| 148 | + //THIS RELIES ON THE LOCAL DIJKSTRA ALGORITHM AVOIDING NEGATIVES | ||
| 149 | + return -1; | ||
| 150 | + } else { | ||
| 151 | + return innerEdgeWeight.weight(edge); | ||
| 152 | + } | ||
| 153 | + } | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + /** | ||
| 157 | + * A result modified to return paths ordered according to the provided comparator. | ||
| 158 | + */ | ||
| 159 | + protected class InnerOrderedResult extends DefaultResult { | ||
| 160 | + | ||
| 161 | + private TreeSet<Path<V, E>> pathSet = new TreeSet<>(new InnerPathComparator()); | ||
| 162 | + | ||
| 163 | + public InnerOrderedResult(V src, V dst) { | ||
| 164 | + super(src, dst); | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + public InnerOrderedResult(V src, V dst, int maxPaths) { | ||
| 168 | + super(src, dst, maxPaths); | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + @Override | ||
| 172 | + public Set<Path<V, E>> paths() { | ||
| 173 | + return ImmutableSet.copyOf(pathSet); | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + /** | ||
| 178 | + * Provides a comparator to order the set of paths. | ||
| 179 | + */ | ||
| 180 | + private class InnerPathComparator implements Comparator<Path<V, E>> { | ||
| 181 | + | ||
| 182 | + @Override | ||
| 183 | + public int compare(Path<V, E> pathOne, Path<V, E> pathTwo) { | ||
| 184 | + int comparisonValue = Double.compare(pathOne.cost(), pathTwo.cost()); | ||
| 185 | + if (comparisonValue != 0) { | ||
| 186 | + return comparisonValue; | ||
| 187 | + } else if (edgeListsAreEqual(pathOne.edges(), pathTwo.edges())) { | ||
| 188 | + return 0; | ||
| 189 | + } else { | ||
| 190 | + return 1; | ||
| 191 | + } | ||
| 192 | + } | ||
| 193 | + } | ||
| 194 | +} |
| 1 | -/* | ||
| 2 | - * Copyright 2014-2015 Open Networking Laboratory | ||
| 3 | - * | ||
| 4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | - * you may not use this file except in compliance with the License. | ||
| 6 | - * You may obtain a copy of the License at | ||
| 7 | - * | ||
| 8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | - * | ||
| 10 | - * Unless required by applicable law or agreed to in writing, software | ||
| 11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | - * See the License for the specific language governing permissions and | ||
| 14 | - * limitations under the License. | ||
| 15 | - */ | ||
| 16 | -package org.onlab.graph; | ||
| 17 | - | ||
| 18 | -import java.util.ArrayList; | ||
| 19 | -//import java.util.HashMap; | ||
| 20 | -import java.util.Iterator; | ||
| 21 | -import java.util.List; | ||
| 22 | -//import java.util.Map; | ||
| 23 | -//import java.util.PriorityQueue; | ||
| 24 | -import java.util.Set; | ||
| 25 | - | ||
| 26 | -import static org.onlab.graph.GraphPathSearch.ALL_PATHS; | ||
| 27 | - | ||
| 28 | -/** | ||
| 29 | - * K-shortest-path graph search algorithm capable of finding not just one, | ||
| 30 | - * but K shortest paths with ascending order between the source and destinations. | ||
| 31 | - */ | ||
| 32 | - | ||
| 33 | -public class KshortestPathSearch<V extends Vertex, E extends Edge<V>> { | ||
| 34 | - | ||
| 35 | - // Define class variables. | ||
| 36 | - private Graph<V, E> immutableGraph; | ||
| 37 | - private MutableGraph<V, E> mutableGraph; | ||
| 38 | - private List<List<E>> pathResults = new ArrayList<List<E>>(); | ||
| 39 | - private List<List<E>> pathCandidates = new ArrayList<List<E>>(); | ||
| 40 | - private V source; | ||
| 41 | - private V sink; | ||
| 42 | - private int numK = 0; | ||
| 43 | - private EdgeWeight<V, E> weight = null; | ||
| 44 | - // private PriorityQueue<List<E>> pathCandidates = new PriorityQueue<List<E>>(); | ||
| 45 | - | ||
| 46 | - // Initialize the graph. | ||
| 47 | - public KshortestPathSearch(Graph<V, E> graph) { | ||
| 48 | - immutableGraph = graph; | ||
| 49 | - mutableGraph = new MutableAdjacencyListsGraph<>(graph.getVertexes(), | ||
| 50 | - graph.getEdges()); | ||
| 51 | - } | ||
| 52 | - | ||
| 53 | - public List<List<E>> search(V src, | ||
| 54 | - V dst, | ||
| 55 | - EdgeWeight<V, E> wei, | ||
| 56 | - int k) { | ||
| 57 | - | ||
| 58 | - weight = wei; | ||
| 59 | - source = src; | ||
| 60 | - sink = dst; | ||
| 61 | - numK = k; | ||
| 62 | - // pathCandidates = new PriorityQueue<List<E>>(); | ||
| 63 | - | ||
| 64 | - pathResults.clear(); | ||
| 65 | - pathCandidates.clear(); | ||
| 66 | - | ||
| 67 | - // Double check the parameters | ||
| 68 | - checkArguments(immutableGraph, src, dst, numK); | ||
| 69 | - | ||
| 70 | - // DefaultResult result = new DefaultResult(src, dst); | ||
| 71 | - | ||
| 72 | - searchKShortestPaths(); | ||
| 73 | - | ||
| 74 | - return pathResults; | ||
| 75 | - } | ||
| 76 | - | ||
| 77 | - private void checkArguments(Graph<V, E> graph, V src, V dst, int k) { | ||
| 78 | - if (graph == null) { | ||
| 79 | - throw new NullPointerException("graph is null"); | ||
| 80 | - } | ||
| 81 | - if (!graph.getVertexes().contains(src)) { | ||
| 82 | - throw new NullPointerException("source node does not exist"); | ||
| 83 | - } | ||
| 84 | - if (!graph.getVertexes().contains(dst)) { | ||
| 85 | - throw new NullPointerException("target node does not exist"); | ||
| 86 | - } | ||
| 87 | - if (k <= 0) { | ||
| 88 | - throw new NullPointerException("K is negative or 0"); | ||
| 89 | - } | ||
| 90 | - if (weight == null) { | ||
| 91 | - throw new NullPointerException("the cost matrix is null"); | ||
| 92 | - } | ||
| 93 | - } | ||
| 94 | - | ||
| 95 | - private void searchKShortestPaths() { | ||
| 96 | - // Step 1: find the shortest path. | ||
| 97 | - List<E> shortestPath = searchShortestPath(immutableGraph, source, sink); | ||
| 98 | - // no path exists, exit. | ||
| 99 | - if (shortestPath == null) { | ||
| 100 | - return; | ||
| 101 | - } | ||
| 102 | - | ||
| 103 | - // Step 2: update the results. | ||
| 104 | - pathResults.add(shortestPath); | ||
| 105 | - // pathCandidates.add(shortestPath); | ||
| 106 | - | ||
| 107 | - // Step 3: find the other K-1 paths. | ||
| 108 | - while (/*pathCandidates.size() > 0 &&*/pathResults.size() < numK) { | ||
| 109 | - // 3.1 the spur node ranges from the first node to the last node in the previous k-shortest path. | ||
| 110 | - List<E> lastPath = pathResults.get(pathResults.size() - 1); | ||
| 111 | - for (int i = 0; i < lastPath.size(); i++) { | ||
| 112 | - // 3.1.1 convert the graph into mutable. | ||
| 113 | - convertGraph(); | ||
| 114 | - // 3.1.2 transform the graph. | ||
| 115 | - List<E> rootPath = createSpurNode(lastPath, i); | ||
| 116 | - transformGraph(rootPath); | ||
| 117 | - // 3.1.3 find the deviation node. | ||
| 118 | - V devNode; | ||
| 119 | - devNode = getDevNode(rootPath); | ||
| 120 | - List<E> spurPath; | ||
| 121 | - // 3.1.4 find the shortest path in the transformed graph. | ||
| 122 | - spurPath = searchShortestPath(mutableGraph, devNode, sink); | ||
| 123 | - // 3.1.5 update the path candidates. | ||
| 124 | - if (spurPath != null) { | ||
| 125 | - // totalPath = rootPath + spurPath; | ||
| 126 | - rootPath.addAll(spurPath); | ||
| 127 | - pathCandidates.add(rootPath); | ||
| 128 | - } | ||
| 129 | - } | ||
| 130 | - // 3.2 if there is no spur path, exit. | ||
| 131 | - if (pathCandidates.size() == 0) { | ||
| 132 | - break; | ||
| 133 | - } | ||
| 134 | - // 3.3 add the path into the results. | ||
| 135 | - addPathResult(); | ||
| 136 | - } | ||
| 137 | - } | ||
| 138 | - | ||
| 139 | - @SuppressWarnings({ "rawtypes", "unchecked" }) | ||
| 140 | - private List<E> searchShortestPath(Graph<V, E> graph, V src, V dst) { | ||
| 141 | - // Determine the shortest path from the source to the destination by using the Dijkstra algorithm. | ||
| 142 | - DijkstraGraphSearch dijkstraAlg = new DijkstraGraphSearch(); | ||
| 143 | - Set<Path> paths = dijkstraAlg.search(graph, src, dst, weight, ALL_PATHS).paths(); | ||
| 144 | - Iterator<Path> itr = paths.iterator(); | ||
| 145 | - if (!itr.hasNext()) { | ||
| 146 | - return null; | ||
| 147 | - } | ||
| 148 | - // return the first shortest path only. | ||
| 149 | - return (List<E>) itr.next().edges(); | ||
| 150 | - } | ||
| 151 | - | ||
| 152 | - private void convertGraph() { | ||
| 153 | - // clear the mutableGraph first | ||
| 154 | - if (mutableGraph != null) { | ||
| 155 | - ((MutableAdjacencyListsGraph) mutableGraph).clear(); | ||
| 156 | - } | ||
| 157 | - | ||
| 158 | - // create a immutableGraph | ||
| 159 | - Set<E> copyEa = immutableGraph.getEdges(); | ||
| 160 | - Set<V> copyVa = immutableGraph.getVertexes(); | ||
| 161 | - for (V vertex : copyVa) { | ||
| 162 | - mutableGraph.addVertex(vertex); | ||
| 163 | - } | ||
| 164 | - for (E edge : copyEa) { | ||
| 165 | - mutableGraph.addEdge(edge); | ||
| 166 | - } | ||
| 167 | - } | ||
| 168 | - | ||
| 169 | - private V getDevNode(List<E> path) { | ||
| 170 | - V srcA; | ||
| 171 | - V dstB; | ||
| 172 | - | ||
| 173 | - if (path.size() == 0) { | ||
| 174 | - return source; | ||
| 175 | - } | ||
| 176 | - | ||
| 177 | - E temp1 = path.get(path.size() - 1); | ||
| 178 | - srcA = temp1.src(); | ||
| 179 | - dstB = temp1.dst(); | ||
| 180 | - | ||
| 181 | - if (path.size() == 1) { | ||
| 182 | - if (srcA.equals(source)) { | ||
| 183 | - return dstB; | ||
| 184 | - } else { | ||
| 185 | - return srcA; | ||
| 186 | - } | ||
| 187 | - } else { | ||
| 188 | - E temp2 = path.get(path.size() - 2); | ||
| 189 | - if (srcA.equals(temp2.src()) || srcA.equals(temp2.dst())) { | ||
| 190 | - return dstB; | ||
| 191 | - } else { | ||
| 192 | - return srcA; | ||
| 193 | - } | ||
| 194 | - } | ||
| 195 | - } | ||
| 196 | - | ||
| 197 | - private List<E> createSpurNode(List<E> path, int n) { | ||
| 198 | - List<E> root = new ArrayList<E>(); | ||
| 199 | - | ||
| 200 | - for (int i = 0; i < n; i++) { | ||
| 201 | - root.add(path.get(i)); | ||
| 202 | - } | ||
| 203 | - return root; | ||
| 204 | - } | ||
| 205 | - | ||
| 206 | - private void transformGraph(List<E> rootPath) { | ||
| 207 | - List<E> prePath; | ||
| 208 | - //remove edges | ||
| 209 | - for (int i = 0; i < pathResults.size(); i++) { | ||
| 210 | - prePath = pathResults.get(i); | ||
| 211 | - if (prePath.size() == 1) { | ||
| 212 | - mutableGraph.removeEdge(prePath.get(0)); | ||
| 213 | - } else if (comparePath(rootPath, prePath)) { | ||
| 214 | - for (int j = 0; j <= rootPath.size(); j++) { | ||
| 215 | - mutableGraph.removeEdge(prePath.get(j)); | ||
| 216 | - } | ||
| 217 | - } | ||
| 218 | - } | ||
| 219 | - for (int i = 0; i < pathCandidates.size(); i++) { | ||
| 220 | - prePath = pathCandidates.get(i); | ||
| 221 | - if (prePath.size() == 1) { | ||
| 222 | - mutableGraph.removeEdge(prePath.get(0)); | ||
| 223 | - } else if (comparePath(rootPath, prePath)) { | ||
| 224 | - for (int j = 0; j <= rootPath.size(); j++) { | ||
| 225 | - mutableGraph.removeEdge(prePath.get(j)); | ||
| 226 | - } | ||
| 227 | - } | ||
| 228 | - } | ||
| 229 | - | ||
| 230 | - if (rootPath.size() == 0) { | ||
| 231 | - return; | ||
| 232 | - } | ||
| 233 | - | ||
| 234 | - //remove nodes | ||
| 235 | - List<V> nodes = new ArrayList<V>(); | ||
| 236 | - nodes.add(source); | ||
| 237 | - V pre = source; | ||
| 238 | - V srcA; | ||
| 239 | - V dstB; | ||
| 240 | - for (int i = 0; i < rootPath.size() - 1; i++) { | ||
| 241 | - E temp = rootPath.get(i); | ||
| 242 | - srcA = temp.src(); | ||
| 243 | - dstB = temp.dst(); | ||
| 244 | - | ||
| 245 | - if (srcA.equals(pre)) { | ||
| 246 | - nodes.add(dstB); | ||
| 247 | - pre = dstB; | ||
| 248 | - } else { | ||
| 249 | - nodes.add(srcA); | ||
| 250 | - pre = srcA; | ||
| 251 | - } | ||
| 252 | - } | ||
| 253 | - for (int i = 0; i < nodes.size(); i++) { | ||
| 254 | - mutableGraph.removeVertex(nodes.get(i)); | ||
| 255 | - } | ||
| 256 | - } | ||
| 257 | - | ||
| 258 | - private boolean comparePath(List<E> path1, List<E> path2) { | ||
| 259 | - if (path1.size() > path2.size()) { | ||
| 260 | - return false; | ||
| 261 | - } | ||
| 262 | - if (path1.size() == 0) { | ||
| 263 | - return true; | ||
| 264 | - } | ||
| 265 | - for (int i = 0; i < path1.size(); i++) { | ||
| 266 | - if (path1.get(i) != path2.get(i)) { | ||
| 267 | - return false; | ||
| 268 | - } | ||
| 269 | - } | ||
| 270 | - return true; | ||
| 271 | - } | ||
| 272 | - | ||
| 273 | - private void addPathResult() { | ||
| 274 | - List<E> sp; | ||
| 275 | - sp = pathCandidates.get(0); | ||
| 276 | - for (int i = 1; i < pathCandidates.size(); i++) { | ||
| 277 | - if (sp.size() > pathCandidates.get(i).size()) { | ||
| 278 | - sp = pathCandidates.get(i); | ||
| 279 | - } | ||
| 280 | - } | ||
| 281 | - pathResults.add(sp); | ||
| 282 | - // Log.info(sp.toString()); | ||
| 283 | - pathCandidates.remove(sp); | ||
| 284 | - } | ||
| 285 | - | ||
| 286 | -} |
| 1 | +package org.onlab.graph; | ||
| 2 | + | ||
| 3 | +import com.google.common.collect.Lists; | ||
| 4 | +import org.junit.Before; | ||
| 5 | +import org.junit.Test; | ||
| 6 | + | ||
| 7 | +import java.util.Iterator; | ||
| 8 | +import java.util.List; | ||
| 9 | +import java.util.Set; | ||
| 10 | + | ||
| 11 | +import static com.google.common.collect.ImmutableSet.of; | ||
| 12 | +import static org.junit.Assert.assertEquals; | ||
| 13 | +import static org.junit.Assert.assertTrue; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * Class for test KshortestPathsSearch. | ||
| 17 | + */ | ||
| 18 | +public class KShortestPathsSearchTest<V extends Vertex, E extends Edge<V>> extends GraphTest { | ||
| 19 | + private KShortestPathsSearch<TestVertex, TestEdge> kShortestPathsSearch = new KShortestPathsSearch<>(); | ||
| 20 | + private GraphPathSearch.Result<TestVertex, TestEdge> result; | ||
| 21 | + | ||
| 22 | + @Before | ||
| 23 | + public void setUp() { | ||
| 24 | + graph = new AdjacencyListsGraph<>(vertexes(), edges()); | ||
| 25 | + } | ||
| 26 | + @Test | ||
| 27 | + public void noPath() { | ||
| 28 | + graph = new AdjacencyListsGraph<>(of(A, B, C, D), | ||
| 29 | + of(new TestEdge(A, B, 1), | ||
| 30 | + new TestEdge(B, A, 1), | ||
| 31 | + new TestEdge(C, D, 1), | ||
| 32 | + new TestEdge(D, C, 1))); | ||
| 33 | + KShortestPathsSearch<TestVertex, TestEdge> kShortestPathsSearch = new KShortestPathsSearch<>(); | ||
| 34 | + GraphPathSearch.Result<TestVertex, TestEdge> result = kShortestPathsSearch.search(graph, A, D, weight, 1); | ||
| 35 | + Set<Path<TestVertex, TestEdge>> resultPathSet = result.paths(); | ||
| 36 | + assertTrue("There should not be any paths.", resultPathSet.isEmpty()); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + @Test | ||
| 40 | + public void testSinglePath() { | ||
| 41 | + //Tests that there is only a single path possible between A and B | ||
| 42 | + graph = new AdjacencyListsGraph<>(vertexes(), edges()); | ||
| 43 | + this.result = kShortestPathsSearch.search(graph, A, B, weight, 2); | ||
| 44 | + Iterator<Path<TestVertex, TestEdge>> itr = result.paths().iterator(); | ||
| 45 | + assertEquals("incorrect paths count", 1, result.paths().size()); | ||
| 46 | + List<TestEdge> correctEdgeList = Lists.newArrayList(); | ||
| 47 | + correctEdgeList.add(new TestEdge(A, B, 1)); | ||
| 48 | + assertTrue("That wrong path was returned.", | ||
| 49 | + edgeListsAreEqual(correctEdgeList, result.paths().iterator().next().edges())); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + @Test | ||
| 53 | + public void testTwoPath() { | ||
| 54 | + //Tests that there are only two paths between A and C and that they are returned in the correct order | ||
| 55 | + result = kShortestPathsSearch.search(graph, A, C, weight, 3); | ||
| 56 | + assertTrue("There are an unexpected number of paths.", result.paths().size() == 2); | ||
| 57 | + Iterator<Path<TestVertex, TestEdge>> edgeListIterator = result.paths().iterator(); | ||
| 58 | + List<TestEdge> correctEdgeList = Lists.newArrayList(); | ||
| 59 | + correctEdgeList.add(new TestEdge(A, B, 1)); | ||
| 60 | + correctEdgeList.add(new TestEdge(B, C, 1)); | ||
| 61 | + assertTrue("The first path from A to C was incorrect.", | ||
| 62 | + edgeListsAreEqual(edgeListIterator.next().edges(), correctEdgeList)); | ||
| 63 | + correctEdgeList.clear(); | ||
| 64 | + correctEdgeList.add(new TestEdge(A, C, 3)); | ||
| 65 | + assertTrue("The second path from A to C was incorrect.", | ||
| 66 | + edgeListsAreEqual(edgeListIterator.next().edges(), correctEdgeList)); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + @Test | ||
| 70 | + public void testFourPath() { | ||
| 71 | + //Tests that there are only four paths between A and E and that they are returned in the correct order | ||
| 72 | + //Also tests the special case where some correct solutions are equal | ||
| 73 | + result = kShortestPathsSearch.search(graph, A, E, weight, 5); | ||
| 74 | + assertTrue("There are an unexpected number of paths.", result.paths().size() == 4); | ||
| 75 | + Iterator<Path<TestVertex, TestEdge>> edgeListIterator = result.paths().iterator(); | ||
| 76 | + List<TestEdge> correctEdgeList = Lists.newArrayList(); | ||
| 77 | + correctEdgeList.add(new TestEdge(A, B, 1)); | ||
| 78 | + correctEdgeList.add(new TestEdge(B, C, 1)); | ||
| 79 | + correctEdgeList.add(new TestEdge(C, E, 1)); | ||
| 80 | + assertTrue("The first path from A to E was incorrect.", | ||
| 81 | + edgeListsAreEqual(edgeListIterator.next().edges(), correctEdgeList)); | ||
| 82 | + correctEdgeList.clear(); | ||
| 83 | + //There are two paths of equal length that should hold positions two and three | ||
| 84 | + List<TestEdge> alternateCorrectEdgeList = Lists.newArrayList(); | ||
| 85 | + correctEdgeList.add(new TestEdge(A, C, 3)); | ||
| 86 | + correctEdgeList.add(new TestEdge(C, E, 1)); | ||
| 87 | + alternateCorrectEdgeList.add(new TestEdge(A, B, 1)); | ||
| 88 | + alternateCorrectEdgeList.add(new TestEdge(B, D, 2)); | ||
| 89 | + alternateCorrectEdgeList.add(new TestEdge(D, E, 1)); | ||
| 90 | + List<TestEdge> candidateOne = edgeListIterator.next().edges(); | ||
| 91 | + List<TestEdge> candidateTwo = edgeListIterator.next().edges(); | ||
| 92 | + if (candidateOne.size() == 2) { | ||
| 93 | + assertTrue("The second path from A to E was incorrect.", | ||
| 94 | + edgeListsAreEqual(candidateOne, correctEdgeList)); | ||
| 95 | + assertTrue("The third path from A to E was incorrect.", | ||
| 96 | + edgeListsAreEqual(candidateTwo, alternateCorrectEdgeList)); | ||
| 97 | + } else { | ||
| 98 | + assertTrue("The second path from A to E was incorrect.", | ||
| 99 | + edgeListsAreEqual(candidateOne, alternateCorrectEdgeList)); | ||
| 100 | + assertTrue("The third path from A to E was incorrect.", | ||
| 101 | + edgeListsAreEqual(candidateTwo, correctEdgeList)); | ||
| 102 | + } | ||
| 103 | + correctEdgeList.clear(); | ||
| 104 | + correctEdgeList.add(new TestEdge(A, B, 1)); | ||
| 105 | + correctEdgeList.add(new TestEdge(B, E, 4)); | ||
| 106 | + assertTrue("The fourth path rom A to E was incorrect", | ||
| 107 | + edgeListsAreEqual(edgeListIterator.next().edges(), correctEdgeList)); | ||
| 108 | + | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + @Test | ||
| 112 | + public void testPathsFromSink() { | ||
| 113 | + //H is a sink in this topology, insure there are no paths from it to any other location | ||
| 114 | + for (TestVertex vertex : vertexes()) { | ||
| 115 | + assertTrue("There should be no paths from vertex H to any other node.", | ||
| 116 | + kShortestPathsSearch.search(graph, H, vertex, weight, 1).paths().size() == 0); | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + @Test | ||
| 121 | + public void testLimitPathSetSize() { | ||
| 122 | + //Checks to make sure that no more than K paths are returned | ||
| 123 | + result = kShortestPathsSearch.search(graph, A, E, weight, 3); | ||
| 124 | + assertTrue("There are an unexpected number of paths.", result.paths().size() == 3); | ||
| 125 | + result = kShortestPathsSearch.search(graph, A, G, weight, 1); | ||
| 126 | + assertTrue("There are an unexpected number of paths.", result.paths().size() == 1); | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + private boolean edgeListsAreEqual(List<TestEdge> edgeListOne, List<TestEdge> edgeListTwo) { | ||
| 130 | + if (edgeListOne.size() != edgeListTwo.size()) { | ||
| 131 | + return false; | ||
| 132 | + } | ||
| 133 | + TestEdge edgeOne; | ||
| 134 | + TestEdge edgeTwo; | ||
| 135 | + for (int i = 0; i < edgeListOne.size(); i++) { | ||
| 136 | + edgeOne = edgeListOne.get(i); | ||
| 137 | + edgeTwo = edgeListTwo.get(i); | ||
| 138 | + if (!edgeOne.equals(edgeTwo)) { | ||
| 139 | + return false; | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + return true; | ||
| 143 | + } | ||
| 144 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | -/* | ||
| 2 | - * Copyright 2014 Open Networking Laboratory | ||
| 3 | - * | ||
| 4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | - * you may not use this file except in compliance with the License. | ||
| 6 | - * You may obtain a copy of the License at | ||
| 7 | - * | ||
| 8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | - * | ||
| 10 | - * Unless required by applicable law or agreed to in writing, software | ||
| 11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | - * See the License for the specific language governing permissions and | ||
| 14 | - * limitations under the License. | ||
| 15 | - */ | ||
| 16 | -package org.onlab.graph; | ||
| 17 | - | ||
| 18 | -import static com.google.common.collect.ImmutableSet.of; | ||
| 19 | -import static org.junit.Assert.*; | ||
| 20 | - | ||
| 21 | -import java.io.ByteArrayOutputStream; | ||
| 22 | -//import java.io.PrintStream; | ||
| 23 | -import java.util.ArrayList; | ||
| 24 | -import java.util.Iterator; | ||
| 25 | -import java.util.List; | ||
| 26 | - | ||
| 27 | -import org.junit.After; | ||
| 28 | -import org.junit.AfterClass; | ||
| 29 | -import org.junit.Before; | ||
| 30 | -import org.junit.BeforeClass; | ||
| 31 | -import org.junit.Test; | ||
| 32 | - | ||
| 33 | -public class KshortestPathSearchTest extends BreadthFirstSearchTest { | ||
| 34 | - | ||
| 35 | - private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); | ||
| 36 | - | ||
| 37 | - @Test | ||
| 38 | - public void noPath() { | ||
| 39 | - graph = new AdjacencyListsGraph<>(of(A, B, C, D), | ||
| 40 | - of(new TestEdge(A, B, 1), | ||
| 41 | - new TestEdge(B, A, 1), | ||
| 42 | - new TestEdge(C, D, 1), | ||
| 43 | - new TestEdge(D, C, 1))); | ||
| 44 | - KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph); | ||
| 45 | - List<List<TestEdge>> result = gs.search(A, D, weight, 1); | ||
| 46 | - List<Path> paths = new ArrayList<>(); | ||
| 47 | - Iterator<List<TestEdge>> itr = result.iterator(); | ||
| 48 | - while (itr.hasNext()) { | ||
| 49 | - System.out.println(itr.next().toString()); | ||
| 50 | - } | ||
| 51 | - assertEquals("incorrect paths count", 0, result.size()); | ||
| 52 | - } | ||
| 53 | - | ||
| 54 | - @Test | ||
| 55 | - public void test2Path() { | ||
| 56 | - graph = new AdjacencyListsGraph<>(of(A, B, C, D), | ||
| 57 | - of(new TestEdge(A, B, 1), | ||
| 58 | - new TestEdge(B, A, 1), | ||
| 59 | - new TestEdge(B, D, 1), | ||
| 60 | - new TestEdge(D, B, 1), | ||
| 61 | - new TestEdge(A, C, 1), | ||
| 62 | - new TestEdge(C, A, 1), | ||
| 63 | - new TestEdge(C, D, 1), | ||
| 64 | - new TestEdge(D, C, 1))); | ||
| 65 | - KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph); | ||
| 66 | - List<List<TestEdge>> result = gs.search(A, D, weight, 2); | ||
| 67 | - List<Path> paths = new ArrayList<>(); | ||
| 68 | - Iterator<List<TestEdge>> itr = result.iterator(); | ||
| 69 | - while (itr.hasNext()) { | ||
| 70 | - System.out.println(itr.next().toString()); | ||
| 71 | - } | ||
| 72 | - assertEquals("incorrect paths count", 2, result.size()); | ||
| 73 | - // assertEquals("printing the paths", outContent.toString()); | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | - @Test | ||
| 77 | - public void test3Path() { | ||
| 78 | - graph = new AdjacencyListsGraph<>(of(A, B, C, D), | ||
| 79 | - of(new TestEdge(A, B, 1), | ||
| 80 | - new TestEdge(B, A, 1), | ||
| 81 | - new TestEdge(A, D, 1), | ||
| 82 | - new TestEdge(D, A, 1), | ||
| 83 | - new TestEdge(B, D, 1), | ||
| 84 | - new TestEdge(D, B, 1), | ||
| 85 | - new TestEdge(A, C, 1), | ||
| 86 | - new TestEdge(C, A, 1), | ||
| 87 | - new TestEdge(C, D, 1), | ||
| 88 | - new TestEdge(D, C, 1))); | ||
| 89 | - KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph); | ||
| 90 | - List<List<TestEdge>> result = gs.search(A, D, weight, 3); | ||
| 91 | - List<Path> paths = new ArrayList<>(); | ||
| 92 | - Iterator<List<TestEdge>> itr = result.iterator(); | ||
| 93 | - while (itr.hasNext()) { | ||
| 94 | - System.out.println(itr.next().toString()); | ||
| 95 | - } | ||
| 96 | - assertEquals("incorrect paths count", 3, result.size()); | ||
| 97 | - // assertEquals("printing the paths", outContent.toString()); | ||
| 98 | - } | ||
| 99 | - | ||
| 100 | - @Test | ||
| 101 | - public void test4Path() { | ||
| 102 | - graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F), | ||
| 103 | - of(new TestEdge(A, B, 1), | ||
| 104 | - new TestEdge(B, A, 1), | ||
| 105 | - new TestEdge(A, C, 1), | ||
| 106 | - new TestEdge(C, A, 1), | ||
| 107 | - new TestEdge(B, D, 1), | ||
| 108 | - new TestEdge(D, B, 1), | ||
| 109 | - new TestEdge(C, E, 1), | ||
| 110 | - new TestEdge(E, C, 1), | ||
| 111 | - new TestEdge(D, F, 1), | ||
| 112 | - new TestEdge(F, D, 1), | ||
| 113 | - new TestEdge(F, E, 1), | ||
| 114 | - new TestEdge(E, F, 1), | ||
| 115 | - new TestEdge(C, D, 1), | ||
| 116 | - new TestEdge(D, C, 1))); | ||
| 117 | - KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph); | ||
| 118 | - List<List<TestEdge>> result = gs.search(A, F, weight, 4); | ||
| 119 | - List<Path> paths = new ArrayList<>(); | ||
| 120 | - Iterator<List<TestEdge>> itr = result.iterator(); | ||
| 121 | - while (itr.hasNext()) { | ||
| 122 | - System.out.println(itr.next().toString()); | ||
| 123 | - } | ||
| 124 | - assertEquals("incorrect paths count", 4, result.size()); | ||
| 125 | - // assertEquals("printing the paths", outContent.toString()); | ||
| 126 | - } | ||
| 127 | - | ||
| 128 | - @Test | ||
| 129 | - public void test6Path() { | ||
| 130 | - graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F), | ||
| 131 | - of(new TestEdge(A, B, 1), | ||
| 132 | - new TestEdge(B, A, 1), | ||
| 133 | - new TestEdge(A, C, 1), | ||
| 134 | - new TestEdge(C, A, 1), | ||
| 135 | - new TestEdge(B, D, 1), | ||
| 136 | - new TestEdge(D, B, 1), | ||
| 137 | - new TestEdge(B, C, 1), | ||
| 138 | - new TestEdge(C, B, 1), | ||
| 139 | - new TestEdge(D, E, 1), | ||
| 140 | - new TestEdge(E, D, 1), | ||
| 141 | - new TestEdge(C, E, 1), | ||
| 142 | - new TestEdge(E, C, 1), | ||
| 143 | - new TestEdge(D, F, 1), | ||
| 144 | - new TestEdge(F, D, 1), | ||
| 145 | - new TestEdge(E, F, 1), | ||
| 146 | - new TestEdge(F, E, 1))); | ||
| 147 | - KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph); | ||
| 148 | - List<List<TestEdge>> result = gs.search(A, F, weight, 6); | ||
| 149 | - List<Path> paths = new ArrayList<>(); | ||
| 150 | - Iterator<List<TestEdge>> itr = result.iterator(); | ||
| 151 | - while (itr.hasNext()) { | ||
| 152 | - System.out.println(itr.next().toString()); | ||
| 153 | - } | ||
| 154 | - assertEquals("incorrect paths count", 6, result.size()); | ||
| 155 | - // assertEquals("printing the paths", outContent.toString()); | ||
| 156 | - } | ||
| 157 | - | ||
| 158 | - @Test | ||
| 159 | - public void dualEdgePath() { | ||
| 160 | - graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H), | ||
| 161 | - of(new TestEdge(A, B, 1), new TestEdge(A, C, 3), | ||
| 162 | - new TestEdge(B, D, 2), new TestEdge(B, C, 1), | ||
| 163 | - new TestEdge(B, E, 4), new TestEdge(C, E, 1), | ||
| 164 | - new TestEdge(D, H, 5), new TestEdge(D, E, 1), | ||
| 165 | - new TestEdge(E, F, 1), new TestEdge(F, D, 1), | ||
| 166 | - new TestEdge(F, G, 1), new TestEdge(F, H, 1), | ||
| 167 | - new TestEdge(A, E, 3), new TestEdge(B, D, 1))); | ||
| 168 | - KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph); | ||
| 169 | - List<List<TestEdge>> result = gs.search(A, G, weight, 6); | ||
| 170 | - List<Path> paths = new ArrayList<>(); | ||
| 171 | - Iterator<List<TestEdge>> itr = result.iterator(); | ||
| 172 | - while (itr.hasNext()) { | ||
| 173 | - System.out.println(itr.next().toString()); | ||
| 174 | - } | ||
| 175 | - assertEquals("incorrect paths count", 6, result.size()); | ||
| 176 | - // assertEquals("printing the paths", outContent.toString()); | ||
| 177 | - } | ||
| 178 | - | ||
| 179 | - @BeforeClass | ||
| 180 | - public static void setUpBeforeClass() throws Exception { | ||
| 181 | - } | ||
| 182 | - | ||
| 183 | - @AfterClass | ||
| 184 | - public static void tearDownAfterClass() throws Exception { | ||
| 185 | - } | ||
| 186 | - | ||
| 187 | - @Before | ||
| 188 | - public void setUp() throws Exception { | ||
| 189 | - // System.setOut(new PrintStream(outContent)); | ||
| 190 | - } | ||
| 191 | - | ||
| 192 | - @After | ||
| 193 | - public void tearDown() throws Exception { | ||
| 194 | - // System.setOut(null); | ||
| 195 | - } | ||
| 196 | - | ||
| 197 | -} |
-
Please register or login to post a comment