weibit

Merge branch 'k-shortest-path'

1 +/*
2 + * Licensed to the Apache Software Foundation (ASF) under one
3 + * or more contributor license agreements. See the NOTICE file
4 + * distributed with this work for additional information
5 + * regarding copyright ownership. The ASF licenses this file
6 + * to you under the Apache License, Version 2.0 (the
7 + * "License"); you may not use this file except in compliance
8 + * with the License. You may obtain a copy of the License at
9 + *
10 + * http://www.apache.org/licenses/LICENSE-2.0
11 + *
12 + * Unless required by applicable law or agreed to in writing,
13 + * software distributed under the License is distributed on an
14 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 + * KIND, either express or implied. See the License for the
16 + * specific language governing permissions and limitations
17 + * under the License.
18 + */
19 +package org.onlab.graph;
20 +
21 +import java.util.ArrayList;
22 +//import java.util.HashMap;
23 +import java.util.Iterator;
24 +import java.util.List;
25 +//import java.util.Map;
26 +//import java.util.PriorityQueue;
27 +import java.util.Set;
28 +
29 +//import org.apache.commons.lang3.tuple.Pair;
30 +//import org.onlab.graph.AbstractGraphPathSearch.DefaultResult;
31 +
32 +/**
33 + * K-shortest-path graph search algorithm capable of finding not just one,
34 + * but K shortest paths with descending order between the source and destinations.
35 + */
36 +
37 +public class KshortestPathSearch<V extends Vertex, E extends Edge<V>> {
38 +
39 + // Define class variables.
40 + private Graph<V, E> immutableGraph;
41 + private MutableGraph<V, E> mutableGraph;
42 + private List<List<E>> pathResults = new ArrayList<List<E>>();
43 + private List<List<E>> pathCandidates = new ArrayList<List<E>>();
44 + private V source;
45 + private V sink;
46 + private int numK = 0;
47 + private EdgeWeight<V, E> weight = null;
48 + // private PriorityQueue<List<E>> pathCandidates = new PriorityQueue<List<E>>();
49 +
50 + // Initialize the graph.
51 + public KshortestPathSearch(Graph<V, E> graph) {
52 + immutableGraph = graph;
53 + mutableGraph = new MutableAdjacencyListsGraph(graph.getVertexes(),
54 + graph.getEdges());
55 + }
56 +
57 + public List<List<E>> search(V src,
58 + V dst,
59 + EdgeWeight<V, E> wei,
60 + int k) {
61 +
62 + weight = wei;
63 + source = src;
64 + sink = dst;
65 + numK = k;
66 + // pathCandidates = new PriorityQueue<List<E>>();
67 +
68 + pathResults.clear();
69 + pathCandidates.clear();
70 +
71 + // Double check the parameters
72 + checkArguments(immutableGraph, src, dst, numK);
73 +
74 + // DefaultResult result = new DefaultResult(src, dst);
75 +
76 + searchKShortestPaths();
77 +
78 + return pathResults;
79 + }
80 +
81 + private void checkArguments(Graph<V, E> graph, V src, V dst, int k) {
82 + if (graph == null) {
83 + throw new NullPointerException("graph is null");
84 + }
85 + if (!graph.getVertexes().contains(src)) {
86 + throw new NullPointerException("source node does not exist");
87 + }
88 + if (!graph.getVertexes().contains(dst)) {
89 + throw new NullPointerException("target node does not exist");
90 + }
91 + if (k <= 0) {
92 + throw new NullPointerException("K is negative or 0");
93 + }
94 + if (weight == null) {
95 + throw new NullPointerException("the cost matrix is null");
96 + }
97 + }
98 +
99 + private void searchKShortestPaths() {
100 + // Step 1: find the shortest path.
101 + List<E> shortestPath = searchShortestPath(immutableGraph, source, sink);
102 + // no path exists, exit.
103 + if (shortestPath == null) {
104 + return;
105 + }
106 +
107 + // Step 2: update the results.
108 + pathResults.add(shortestPath);
109 + // pathCandidates.add(shortestPath);
110 +
111 + // Step 3: find the other K-1 paths.
112 + while (/*pathCandidates.size() > 0 &&*/pathResults.size() < numK) {
113 + // 3.1 the spur node ranges from the first node to the last node in the previous k-shortest path.
114 + List<E> lastPath = pathResults.get(pathResults.size() - 1);
115 + for (int i = 0; i < lastPath.size(); i++) {
116 + // 4.1 convert the graph into mutable.
117 + convertGraph();
118 + // 4.2 transform the graph.
119 + List<E> rootPath = createSpurNode(lastPath, i);
120 + transformGraph(rootPath);
121 + // 4.3 find the deviation node.
122 + V devNode;
123 + devNode = getDevNode(rootPath);
124 + List<E> spurPath;
125 + // 4.4 find the shortest path in the transformed graph.
126 + spurPath = searchShortestPath(mutableGraph, devNode, sink);
127 + // 4.5 update the path candidates.
128 + if (spurPath != null) {
129 + // totalPath = rootPath + spurPath;
130 + rootPath.addAll(spurPath);
131 + pathCandidates.add(rootPath);
132 + }
133 + }
134 + // 3.2 if there is no spur path, exit.
135 + if (pathCandidates.size() == 0) {
136 + break;
137 + }
138 + // 3.3 add the path into the results.
139 + addPathResult();
140 + }
141 + }
142 +
143 + private List<E> searchShortestPath(Graph<V, E> graph, V src, V dst) {
144 + // Determine the shortest path from the source to the destination by using the Dijkstra algorithm.
145 + DijkstraGraphSearch dijkstraAlg = new DijkstraGraphSearch();
146 + Set<Path> paths = dijkstraAlg.search(graph, src, dst, weight).paths();
147 + Iterator<Path> itr = paths.iterator();
148 + if (!itr.hasNext()) {
149 + return null;
150 + }
151 + // return the first shortest path only.
152 + return (List<E>) itr.next().edges();
153 + }
154 +
155 + private void convertGraph() {
156 + // clear the mutableGraph first
157 + if (mutableGraph != null) {
158 + ((MutableAdjacencyListsGraph) mutableGraph).clear();
159 + }
160 +
161 + // create a immutableGraph
162 + Set<E> copyEa = immutableGraph.getEdges();
163 + Set<V> copyVa = immutableGraph.getVertexes();
164 + for (V vertex : copyVa) {
165 + mutableGraph.addVertex(vertex);
166 + }
167 + for (E edge : copyEa) {
168 + mutableGraph.addEdge(edge);
169 + }
170 + }
171 +
172 + private V getDevNode(List<E> path) {
173 + V srcA;
174 + V dstB;
175 +
176 + if (path.size() == 0) {
177 + return source;
178 + }
179 +
180 + E temp1 = path.get(path.size() - 1);
181 + srcA = temp1.src();
182 + dstB = temp1.dst();
183 +
184 + if (path.size() == 1) {
185 + if (srcA.equals(source)) {
186 + return dstB;
187 + } else {
188 + return srcA;
189 + }
190 + } else {
191 + E temp2 = path.get(path.size() - 2);
192 + if (srcA.equals(temp2.src()) || srcA.equals(temp2.dst())) {
193 + return dstB;
194 + } else {
195 + return srcA;
196 + }
197 + }
198 + }
199 +
200 + private List<E> createSpurNode(List<E> path, int n) {
201 + List<E> root = new ArrayList<E>();
202 +
203 + for (int i = 0; i < n; i++) {
204 + root.add(path.get(i));
205 + }
206 + return root;
207 + }
208 +
209 + private void transformGraph(List<E> rootPath) {
210 + List<E> prePath;
211 + //remove edges
212 + for (int i = 0; i < pathResults.size(); i++) {
213 + prePath = pathResults.get(i);
214 + if (prePath.size() == 1) {
215 + mutableGraph.removeEdge(prePath.get(0));
216 + } else if (comparePath(rootPath, prePath)) {
217 + for (int j = 0; j <= rootPath.size(); j++) {
218 + mutableGraph.removeEdge(prePath.get(j));
219 + }
220 + }
221 + }
222 + for (int i = 0; i < pathCandidates.size(); i++) {
223 + prePath = pathCandidates.get(i);
224 + if (prePath.size() == 1) {
225 + mutableGraph.removeEdge(prePath.get(0));
226 + } else if (comparePath(rootPath, prePath)) {
227 + for (int j = 0; j <= rootPath.size(); j++) {
228 + mutableGraph.removeEdge(prePath.get(j));
229 + }
230 + }
231 + }
232 +
233 + if (rootPath.size() == 0) {
234 + return;
235 + }
236 +
237 + //remove nodes
238 + List<V> nodes = new ArrayList<V>();
239 + nodes.add(source);
240 + V pre = source;
241 + V srcA;
242 + V dstB;
243 + for (int i = 0; i < rootPath.size() - 1; i++) {
244 + E temp = rootPath.get(i);
245 + srcA = temp.src();
246 + dstB = temp.dst();
247 +
248 + if (srcA.equals(pre)) {
249 + nodes.add(dstB);
250 + pre = dstB;
251 + } else {
252 + nodes.add(srcA);
253 + pre = srcA;
254 + }
255 + }
256 + for (int i = 0; i < nodes.size(); i++) {
257 + mutableGraph.removeVertex(nodes.get(i));
258 + }
259 + }
260 +
261 + private boolean comparePath(List<E> path1, List<E> path2) {
262 + if (path1.size() > path2.size()) {
263 + return false;
264 + }
265 + if (path1.size() == 0) {
266 + return true;
267 + }
268 + for (int i = 0; i < path1.size(); i++) {
269 + if (path1.get(i) != path2.get(i)) {
270 + return false;
271 + }
272 + }
273 + return true;
274 + }
275 +
276 + private void addPathResult() {
277 + List<E> sp;
278 + sp = pathCandidates.get(0);
279 + for (int i = 1; i < pathCandidates.size(); i++) {
280 + if (sp.size() > pathCandidates.get(i).size()) {
281 + sp = pathCandidates.get(i);
282 + }
283 + }
284 + pathResults.add(sp);
285 + // Log.info(sp.toString());
286 + pathCandidates.remove(sp);
287 + }
288 +
289 +}
1 +package org.onlab.graph;
2 +
3 +import static com.google.common.base.MoreObjects.toStringHelper;
4 +
5 +import java.util.HashSet;
6 +import java.util.Objects;
7 +import java.util.Set;
8 +
9 +import com.google.common.collect.HashMultimap;
10 +import com.google.common.collect.SetMultimap;
11 +
12 +public class MutableAdjacencyListsGraph<V extends Vertex, E extends Edge<V>>
13 +implements MutableGraph<V, E> {
14 + private Set<V> vertexes = new HashSet<V>();
15 + private Set<E> edges = new HashSet<E>();
16 +
17 + private SetMultimap<V, E> sources = HashMultimap.create();
18 + private SetMultimap<V, E> destinations = HashMultimap.create();
19 +
20 + /**
21 + * Creates a graph comprising of the specified vertexes and edges.
22 + *
23 + * @param vertexes set of graph vertexes
24 + * @param edges set of graph edges
25 + */
26 + public MutableAdjacencyListsGraph(Set<V> vertex, Set<E> edge) {
27 + vertexes.addAll(vertex);
28 + edges.addAll(edge);
29 + for (E e : edge) {
30 + sources.put(e.src(), e);
31 + vertexes.add(e.src());
32 + destinations.put(e.dst(), e);
33 + vertexes.add(e.dst());
34 + }
35 + }
36 +
37 + @Override
38 + public Set<V> getVertexes() {
39 + return vertexes;
40 + }
41 +
42 + @Override
43 + public Set<E> getEdges() {
44 + return edges;
45 + }
46 +
47 + @Override
48 + public Set<E> getEdgesFrom(V src) {
49 + return sources.get(src);
50 + }
51 +
52 + @Override
53 + public Set<E> getEdgesTo(V dst) {
54 + return destinations.get(dst);
55 + }
56 +
57 + @Override
58 + public boolean equals(Object obj) {
59 + if (this == obj) {
60 + return true;
61 + }
62 + if (obj instanceof MutableAdjacencyListsGraph) {
63 + MutableAdjacencyListsGraph that = (MutableAdjacencyListsGraph) obj;
64 + return this.getClass() == that.getClass() &&
65 + Objects.equals(this.vertexes, that.vertexes) &&
66 + Objects.equals(this.edges, that.edges);
67 + }
68 + return false;
69 + }
70 +
71 + @Override
72 + public int hashCode() {
73 + return Objects.hash(vertexes, edges);
74 + }
75 +
76 + @Override
77 + public String toString() {
78 + return toStringHelper(this)
79 + .add("vertexes", vertexes)
80 + .add("edges", edges)
81 + .toString();
82 + }
83 +
84 +
85 + @Override
86 + public void addVertex(V vertex) {
87 + if (vertexes != null) {
88 + if (!vertexes.contains(vertex)) {
89 + vertexes.add(vertex);
90 + }
91 + }
92 + }
93 +
94 + @Override
95 + public void removeVertex(V vertex) {
96 + // TODO Auto-generated method stub
97 + if (vertexes != null && edges != null) {
98 + if (vertexes.contains(vertex)) {
99 + vertexes.remove(vertex);
100 + Set<E> srcEdgesList = sources.get(vertex);
101 + Set<E> dstEdgesList = destinations.get(vertex);
102 + edges.removeAll(srcEdgesList);
103 + edges.removeAll(dstEdgesList);
104 + sources.remove(vertex, srcEdgesList);
105 + sources.remove(vertex, dstEdgesList);
106 + }
107 + }
108 + }
109 +
110 + @Override
111 + public void addEdge(E edge) {
112 + if (edges != null) {
113 + if (!edges.contains(edge)) {
114 + edges.add(edge);
115 + sources.put(edge.src(), edge);
116 + destinations.put(edge.dst(), edge);
117 + }
118 + }
119 + }
120 +
121 + @Override
122 + public void removeEdge(E edge) {
123 + if (edges != null) {
124 + if (edges.contains(edge)) {
125 + edges.remove(edge);
126 + sources.remove(edge.src(), edge);
127 + destinations.remove(edge.dst(), edge);
128 + }
129 + }
130 + }
131 +
132 + @Override
133 + public Graph<V, E> toImmutable() {
134 + // TODO Auto-generated method stub
135 + return null;
136 + }
137 +
138 + /**
139 + * Clear the graph.
140 + */
141 + public void clear() {
142 + edges.clear();
143 + vertexes.clear();
144 + sources.clear();
145 + destinations.clear();
146 + }
147 +}
1 +/*
2 + * Licensed to the Apache Software Foundation (ASF) under one
3 + * or more contributor license agreements. See the NOTICE file
4 + * distributed with this work for additional information
5 + * regarding copyright ownership. The ASF licenses this file
6 + * to you under the Apache License, Version 2.0 (the
7 + * "License"); you may not use this file except in compliance
8 + * with the License. You may obtain a copy of the License at
9 + *
10 + * http://www.apache.org/licenses/LICENSE-2.0
11 + *
12 + * Unless required by applicable law or agreed to in writing,
13 + * software distributed under the License is distributed on an
14 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 + * KIND, either express or implied. See the License for the
16 + * specific language governing permissions and limitations
17 + * under the License.
18 + */
19 +package org.onlab.graph;
20 +
21 +import static com.google.common.collect.ImmutableSet.of;
22 +import static org.junit.Assert.*;
23 +
24 +import java.io.ByteArrayOutputStream;
25 +//import java.io.PrintStream;
26 +import java.util.ArrayList;
27 +import java.util.Iterator;
28 +import java.util.List;
29 +
30 +import org.junit.After;
31 +import org.junit.AfterClass;
32 +import org.junit.Before;
33 +import org.junit.BeforeClass;
34 +import org.junit.Test;
35 +
36 +public class KshortestPathSearchTest extends BreadthFirstSearchTest {
37 +
38 + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
39 +
40 + @Test
41 + public void noPath() {
42 + graph = new AdjacencyListsGraph<>(of(A, B, C, D),
43 + of(new TestEdge(A, B, 1),
44 + new TestEdge(B, A, 1),
45 + new TestEdge(C, D, 1),
46 + new TestEdge(D, C, 1)));
47 + KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
48 + List<List<TestEdge>> result = gs.search(A, D, weight, 1);
49 + List<Path> paths = new ArrayList<>();
50 + Iterator<List<TestEdge>> itr = result.iterator();
51 + while (itr.hasNext()) {
52 + System.out.println(itr.next().toString());
53 + }
54 + assertEquals("incorrect paths count", 0, result.size());
55 + }
56 +
57 + @Test
58 + public void test2Path() {
59 + graph = new AdjacencyListsGraph<>(of(A, B, C, D),
60 + of(new TestEdge(A, B, 1),
61 + new TestEdge(B, A, 1),
62 + new TestEdge(B, D, 1),
63 + new TestEdge(D, B, 1),
64 + new TestEdge(A, C, 1),
65 + new TestEdge(C, A, 1),
66 + new TestEdge(C, D, 1),
67 + new TestEdge(D, C, 1)));
68 + KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
69 + List<List<TestEdge>> result = gs.search(A, D, weight, 2);
70 + List<Path> paths = new ArrayList<>();
71 + Iterator<List<TestEdge>> itr = result.iterator();
72 + while (itr.hasNext()) {
73 + System.out.println(itr.next().toString());
74 + }
75 + assertEquals("incorrect paths count", 2, result.size());
76 + // assertEquals("printing the paths", outContent.toString());
77 + }
78 +
79 + @Test
80 + public void test3Path() {
81 + graph = new AdjacencyListsGraph<>(of(A, B, C, D),
82 + of(new TestEdge(A, B, 1),
83 + new TestEdge(B, A, 1),
84 + new TestEdge(A, D, 1),
85 + new TestEdge(D, A, 1),
86 + new TestEdge(B, D, 1),
87 + new TestEdge(D, B, 1),
88 + new TestEdge(A, C, 1),
89 + new TestEdge(C, A, 1),
90 + new TestEdge(C, D, 1),
91 + new TestEdge(D, C, 1)));
92 + KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
93 + List<List<TestEdge>> result = gs.search(A, D, weight, 3);
94 + List<Path> paths = new ArrayList<>();
95 + Iterator<List<TestEdge>> itr = result.iterator();
96 + while (itr.hasNext()) {
97 + System.out.println(itr.next().toString());
98 + }
99 + assertEquals("incorrect paths count", 3, result.size());
100 + // assertEquals("printing the paths", outContent.toString());
101 + }
102 +
103 + @Test
104 + public void test4Path() {
105 + graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F),
106 + of(new TestEdge(A, B, 1),
107 + new TestEdge(B, A, 1),
108 + new TestEdge(A, C, 1),
109 + new TestEdge(C, A, 1),
110 + new TestEdge(B, D, 1),
111 + new TestEdge(D, B, 1),
112 + new TestEdge(C, E, 1),
113 + new TestEdge(E, C, 1),
114 + new TestEdge(D, F, 1),
115 + new TestEdge(F, D, 1),
116 + new TestEdge(F, E, 1),
117 + new TestEdge(E, F, 1),
118 + new TestEdge(C, D, 1),
119 + new TestEdge(D, C, 1)));
120 + KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
121 + List<List<TestEdge>> result = gs.search(A, F, weight, 4);
122 + List<Path> paths = new ArrayList<>();
123 + Iterator<List<TestEdge>> itr = result.iterator();
124 + while (itr.hasNext()) {
125 + System.out.println(itr.next().toString());
126 + }
127 + assertEquals("incorrect paths count", 4, result.size());
128 + // assertEquals("printing the paths", outContent.toString());
129 + }
130 +
131 + @Test
132 + public void test6Path() {
133 + graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F),
134 + of(new TestEdge(A, B, 1),
135 + new TestEdge(B, A, 1),
136 + new TestEdge(A, C, 1),
137 + new TestEdge(C, A, 1),
138 + new TestEdge(B, D, 1),
139 + new TestEdge(D, B, 1),
140 + new TestEdge(B, C, 1),
141 + new TestEdge(C, B, 1),
142 + new TestEdge(D, E, 1),
143 + new TestEdge(E, D, 1),
144 + new TestEdge(C, E, 1),
145 + new TestEdge(E, C, 1),
146 + new TestEdge(D, F, 1),
147 + new TestEdge(F, D, 1),
148 + new TestEdge(E, F, 1),
149 + new TestEdge(F, E, 1)));
150 + KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
151 + List<List<TestEdge>> result = gs.search(A, F, weight, 6);
152 + List<Path> paths = new ArrayList<>();
153 + Iterator<List<TestEdge>> itr = result.iterator();
154 + while (itr.hasNext()) {
155 + System.out.println(itr.next().toString());
156 + }
157 + assertEquals("incorrect paths count", 6, result.size());
158 + // assertEquals("printing the paths", outContent.toString());
159 + }
160 +
161 + @Test
162 + public void dualEdgePath() {
163 + graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H),
164 + of(new TestEdge(A, B, 1), new TestEdge(A, C, 3),
165 + new TestEdge(B, D, 2), new TestEdge(B, C, 1),
166 + new TestEdge(B, E, 4), new TestEdge(C, E, 1),
167 + new TestEdge(D, H, 5), new TestEdge(D, E, 1),
168 + new TestEdge(E, F, 1), new TestEdge(F, D, 1),
169 + new TestEdge(F, G, 1), new TestEdge(F, H, 1),
170 + new TestEdge(A, E, 3), new TestEdge(B, D, 1)));
171 + KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
172 + List<List<TestEdge>> result = gs.search(A, G, weight, 6);
173 + List<Path> paths = new ArrayList<>();
174 + Iterator<List<TestEdge>> itr = result.iterator();
175 + while (itr.hasNext()) {
176 + System.out.println(itr.next().toString());
177 + }
178 + assertEquals("incorrect paths count", 6, result.size());
179 + // assertEquals("printing the paths", outContent.toString());
180 + }
181 +
182 + @BeforeClass
183 + public static void setUpBeforeClass() throws Exception {
184 + }
185 +
186 + @AfterClass
187 + public static void tearDownAfterClass() throws Exception {
188 + }
189 +
190 + @Before
191 + public void setUp() throws Exception {
192 + // System.setOut(new PrintStream(outContent));
193 + }
194 +
195 + @After
196 + public void tearDown() throws Exception {
197 + // System.setOut(null);
198 + }
199 +
200 +}