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