weibit

K-shorest path algorithm to support UC1, it can be used by other modules as well.

Change-Id: I736ec55c6211a505d6cf43ab22e1197fdb86ecf3
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.onlab.graph;
import java.util.ArrayList;
//import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
//import java.util.Map;
//import java.util.PriorityQueue;
import java.util.Set;
//import org.apache.commons.lang3.tuple.Pair;
//import org.onlab.graph.AbstractGraphPathSearch.DefaultResult;
/**
* K-shortest-path graph search algorithm capable of finding not just one,
* but K shortest paths with descending order between the source and destinations.
*/
public class KshortestPathSearch<V extends Vertex, E extends Edge<V>> {
// Define class variables.
private Graph<V, E> immutableGraph;
private MutableGraph<V, E> mutableGraph;
private List<List<E>> pathResults = new ArrayList<List<E>>();
private List<List<E>> pathCandidates = new ArrayList<List<E>>();
private V source;
private V sink;
private int numK = 0;
private EdgeWeight<V, E> weight = null;
// private PriorityQueue<List<E>> pathCandidates = new PriorityQueue<List<E>>();
// Initialize the graph.
public KshortestPathSearch(Graph<V, E> graph) {
immutableGraph = graph;
mutableGraph = new MutableAdjacencyListsGraph(graph.getVertexes(),
graph.getEdges());
}
public List<List<E>> search(V src,
V dst,
EdgeWeight<V, E> wei,
int k) {
weight = wei;
source = src;
sink = dst;
numK = k;
// pathCandidates = new PriorityQueue<List<E>>();
pathResults.clear();
pathCandidates.clear();
// Double check the parameters
checkArguments(immutableGraph, src, dst, numK);
// DefaultResult result = new DefaultResult(src, dst);
searchKShortestPaths();
return pathResults;
}
private void checkArguments(Graph<V, E> graph, V src, V dst, int k) {
if (graph == null) {
throw new NullPointerException("graph is null");
}
if (!graph.getVertexes().contains(src)) {
throw new NullPointerException("source node does not exist");
}
if (!graph.getVertexes().contains(dst)) {
throw new NullPointerException("target node does not exist");
}
if (k <= 0) {
throw new NullPointerException("K is negative or 0");
}
if (weight == null) {
throw new NullPointerException("the cost matrix is null");
}
}
private void searchKShortestPaths() {
// Step 1: find the shortest path.
List<E> shortestPath = searchShortestPath(immutableGraph, source, sink);
// no path exists, exit.
if (shortestPath == null) {
return;
}
// Step 2: update the results.
pathResults.add(shortestPath);
// pathCandidates.add(shortestPath);
// Step 3: find the other K-1 paths.
while (/*pathCandidates.size() > 0 &&*/pathResults.size() < numK) {
// 3.1 the spur node ranges from the first node to the last node in the previous k-shortest path.
List<E> lastPath = pathResults.get(pathResults.size() - 1);
for (int i = 0; i < lastPath.size(); i++) {
// 4.1 convert the graph into mutable.
convertGraph();
// 4.2 transform the graph.
List<E> rootPath = createSpurNode(lastPath, i);
transformGraph(rootPath);
// 4.3 find the deviation node.
V devNode;
devNode = getDevNode(rootPath);
List<E> spurPath;
// 4.4 find the shortest path in the transformed graph.
spurPath = searchShortestPath(mutableGraph, devNode, sink);
// 4.5 update the path candidates.
if (spurPath != null) {
// totalPath = rootPath + spurPath;
rootPath.addAll(spurPath);
pathCandidates.add(rootPath);
}
}
// 3.2 if there is no spur path, exit.
if (pathCandidates.size() == 0) {
break;
}
// 3.3 add the path into the results.
addPathResult();
}
}
private List<E> searchShortestPath(Graph<V, E> graph, V src, V dst) {
// Determine the shortest path from the source to the destination by using the Dijkstra algorithm.
DijkstraGraphSearch dijkstraAlg = new DijkstraGraphSearch();
Set<Path> paths = dijkstraAlg.search(graph, src, dst, weight).paths();
Iterator<Path> itr = paths.iterator();
if (!itr.hasNext()) {
return null;
}
// return the first shortest path only.
return (List<E>) itr.next().edges();
}
private void convertGraph() {
// clear the mutableGraph first
if (mutableGraph != null) {
((MutableAdjacencyListsGraph) mutableGraph).clear();
}
// create a immutableGraph
Set<E> copyEa = immutableGraph.getEdges();
Set<V> copyVa = immutableGraph.getVertexes();
for (V vertex : copyVa) {
mutableGraph.addVertex(vertex);
}
for (E edge : copyEa) {
mutableGraph.addEdge(edge);
}
}
private V getDevNode(List<E> path) {
V srcA;
V dstB;
if (path.size() == 0) {
return source;
}
E temp1 = path.get(path.size() - 1);
srcA = temp1.src();
dstB = temp1.dst();
if (path.size() == 1) {
if (srcA.equals(source)) {
return dstB;
} else {
return srcA;
}
} else {
E temp2 = path.get(path.size() - 2);
if (srcA.equals(temp2.src()) || srcA.equals(temp2.dst())) {
return dstB;
} else {
return srcA;
}
}
}
private List<E> createSpurNode(List<E> path, int n) {
List<E> root = new ArrayList<E>();
for (int i = 0; i < n; i++) {
root.add(path.get(i));
}
return root;
}
private void transformGraph(List<E> rootPath) {
List<E> prePath;
//remove edges
for (int i = 0; i < pathResults.size(); i++) {
prePath = pathResults.get(i);
if (prePath.size() == 1) {
mutableGraph.removeEdge(prePath.get(0));
} else if (comparePath(rootPath, prePath)) {
for (int j = 0; j <= rootPath.size(); j++) {
mutableGraph.removeEdge(prePath.get(j));
}
}
}
for (int i = 0; i < pathCandidates.size(); i++) {
prePath = pathCandidates.get(i);
if (prePath.size() == 1) {
mutableGraph.removeEdge(prePath.get(0));
} else if (comparePath(rootPath, prePath)) {
for (int j = 0; j <= rootPath.size(); j++) {
mutableGraph.removeEdge(prePath.get(j));
}
}
}
if (rootPath.size() == 0) {
return;
}
//remove nodes
List<V> nodes = new ArrayList<V>();
nodes.add(source);
V pre = source;
V srcA;
V dstB;
for (int i = 0; i < rootPath.size() - 1; i++) {
E temp = rootPath.get(i);
srcA = temp.src();
dstB = temp.dst();
if (srcA.equals(pre)) {
nodes.add(dstB);
pre = dstB;
} else {
nodes.add(srcA);
pre = srcA;
}
}
for (int i = 0; i < nodes.size(); i++) {
mutableGraph.removeVertex(nodes.get(i));
}
}
private boolean comparePath(List<E> path1, List<E> path2) {
if (path1.size() > path2.size()) {
return false;
}
if (path1.size() == 0) {
return true;
}
for (int i = 0; i < path1.size(); i++) {
if (path1.get(i) != path2.get(i)) {
return false;
}
}
return true;
}
private void addPathResult() {
List<E> sp;
sp = pathCandidates.get(0);
for (int i = 1; i < pathCandidates.size(); i++) {
if (sp.size() > pathCandidates.get(i).size()) {
sp = pathCandidates.get(i);
}
}
pathResults.add(sp);
// Log.info(sp.toString());
pathCandidates.remove(sp);
}
}
package org.onlab.graph;
import static com.google.common.base.MoreObjects.toStringHelper;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
public class MutableAdjacencyListsGraph<V extends Vertex, E extends Edge<V>>
implements MutableGraph<V, E> {
private Set<V> vertexes = new HashSet<V>();
private Set<E> edges = new HashSet<E>();
private SetMultimap<V, E> sources = HashMultimap.create();
private SetMultimap<V, E> destinations = HashMultimap.create();
/**
* Creates a graph comprising of the specified vertexes and edges.
*
* @param vertexes set of graph vertexes
* @param edges set of graph edges
*/
public MutableAdjacencyListsGraph(Set<V> vertex, Set<E> edge) {
vertexes.addAll(vertex);
edges.addAll(edge);
for (E e : edge) {
sources.put(e.src(), e);
vertexes.add(e.src());
destinations.put(e.dst(), e);
vertexes.add(e.dst());
}
}
@Override
public Set<V> getVertexes() {
return vertexes;
}
@Override
public Set<E> getEdges() {
return edges;
}
@Override
public Set<E> getEdgesFrom(V src) {
return sources.get(src);
}
@Override
public Set<E> getEdgesTo(V dst) {
return destinations.get(dst);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof MutableAdjacencyListsGraph) {
MutableAdjacencyListsGraph that = (MutableAdjacencyListsGraph) obj;
return this.getClass() == that.getClass() &&
Objects.equals(this.vertexes, that.vertexes) &&
Objects.equals(this.edges, that.edges);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(vertexes, edges);
}
@Override
public String toString() {
return toStringHelper(this)
.add("vertexes", vertexes)
.add("edges", edges)
.toString();
}
@Override
public void addVertex(V vertex) {
if (vertexes != null) {
if (!vertexes.contains(vertex)) {
vertexes.add(vertex);
}
}
}
@Override
public void removeVertex(V vertex) {
// TODO Auto-generated method stub
if (vertexes != null && edges != null) {
if (vertexes.contains(vertex)) {
vertexes.remove(vertex);
Set<E> srcEdgesList = sources.get(vertex);
Set<E> dstEdgesList = destinations.get(vertex);
edges.removeAll(srcEdgesList);
edges.removeAll(dstEdgesList);
sources.remove(vertex, srcEdgesList);
sources.remove(vertex, dstEdgesList);
}
}
}
@Override
public void addEdge(E edge) {
if (edges != null) {
if (!edges.contains(edge)) {
edges.add(edge);
sources.put(edge.src(), edge);
destinations.put(edge.dst(), edge);
}
}
}
@Override
public void removeEdge(E edge) {
if (edges != null) {
if (edges.contains(edge)) {
edges.remove(edge);
sources.remove(edge.src(), edge);
destinations.remove(edge.dst(), edge);
}
}
}
@Override
public Graph<V, E> toImmutable() {
// TODO Auto-generated method stub
return null;
}
/**
* Clear the graph.
*/
public void clear() {
edges.clear();
vertexes.clear();
sources.clear();
destinations.clear();
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.onlab.graph;
import static com.google.common.collect.ImmutableSet.of;
import static org.junit.Assert.*;
import java.io.ByteArrayOutputStream;
//import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class KshortestPathSearchTest extends BreadthFirstSearchTest {
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
@Test
public void noPath() {
graph = new AdjacencyListsGraph<>(of(A, B, C, D),
of(new TestEdge(A, B, 1),
new TestEdge(B, A, 1),
new TestEdge(C, D, 1),
new TestEdge(D, C, 1)));
KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
List<List<TestEdge>> result = gs.search(A, D, weight, 1);
List<Path> paths = new ArrayList<>();
Iterator<List<TestEdge>> itr = result.iterator();
while (itr.hasNext()) {
System.out.println(itr.next().toString());
}
assertEquals("incorrect paths count", 0, result.size());
}
@Test
public void test2Path() {
graph = new AdjacencyListsGraph<>(of(A, B, C, D),
of(new TestEdge(A, B, 1),
new TestEdge(B, A, 1),
new TestEdge(B, D, 1),
new TestEdge(D, B, 1),
new TestEdge(A, C, 1),
new TestEdge(C, A, 1),
new TestEdge(C, D, 1),
new TestEdge(D, C, 1)));
KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
List<List<TestEdge>> result = gs.search(A, D, weight, 2);
List<Path> paths = new ArrayList<>();
Iterator<List<TestEdge>> itr = result.iterator();
while (itr.hasNext()) {
System.out.println(itr.next().toString());
}
assertEquals("incorrect paths count", 2, result.size());
// assertEquals("printing the paths", outContent.toString());
}
@Test
public void test3Path() {
graph = new AdjacencyListsGraph<>(of(A, B, C, D),
of(new TestEdge(A, B, 1),
new TestEdge(B, A, 1),
new TestEdge(A, D, 1),
new TestEdge(D, A, 1),
new TestEdge(B, D, 1),
new TestEdge(D, B, 1),
new TestEdge(A, C, 1),
new TestEdge(C, A, 1),
new TestEdge(C, D, 1),
new TestEdge(D, C, 1)));
KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
List<List<TestEdge>> result = gs.search(A, D, weight, 3);
List<Path> paths = new ArrayList<>();
Iterator<List<TestEdge>> itr = result.iterator();
while (itr.hasNext()) {
System.out.println(itr.next().toString());
}
assertEquals("incorrect paths count", 3, result.size());
// assertEquals("printing the paths", outContent.toString());
}
@Test
public void test4Path() {
graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F),
of(new TestEdge(A, B, 1),
new TestEdge(B, A, 1),
new TestEdge(A, C, 1),
new TestEdge(C, A, 1),
new TestEdge(B, D, 1),
new TestEdge(D, B, 1),
new TestEdge(C, E, 1),
new TestEdge(E, C, 1),
new TestEdge(D, F, 1),
new TestEdge(F, D, 1),
new TestEdge(F, E, 1),
new TestEdge(E, F, 1),
new TestEdge(C, D, 1),
new TestEdge(D, C, 1)));
KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
List<List<TestEdge>> result = gs.search(A, F, weight, 4);
List<Path> paths = new ArrayList<>();
Iterator<List<TestEdge>> itr = result.iterator();
while (itr.hasNext()) {
System.out.println(itr.next().toString());
}
assertEquals("incorrect paths count", 4, result.size());
// assertEquals("printing the paths", outContent.toString());
}
@Test
public void test6Path() {
graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F),
of(new TestEdge(A, B, 1),
new TestEdge(B, A, 1),
new TestEdge(A, C, 1),
new TestEdge(C, A, 1),
new TestEdge(B, D, 1),
new TestEdge(D, B, 1),
new TestEdge(B, C, 1),
new TestEdge(C, B, 1),
new TestEdge(D, E, 1),
new TestEdge(E, D, 1),
new TestEdge(C, E, 1),
new TestEdge(E, C, 1),
new TestEdge(D, F, 1),
new TestEdge(F, D, 1),
new TestEdge(E, F, 1),
new TestEdge(F, E, 1)));
KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
List<List<TestEdge>> result = gs.search(A, F, weight, 6);
List<Path> paths = new ArrayList<>();
Iterator<List<TestEdge>> itr = result.iterator();
while (itr.hasNext()) {
System.out.println(itr.next().toString());
}
assertEquals("incorrect paths count", 6, result.size());
// assertEquals("printing the paths", outContent.toString());
}
@Test
public void dualEdgePath() {
graph = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H),
of(new TestEdge(A, B, 1), new TestEdge(A, C, 3),
new TestEdge(B, D, 2), new TestEdge(B, C, 1),
new TestEdge(B, E, 4), new TestEdge(C, E, 1),
new TestEdge(D, H, 5), new TestEdge(D, E, 1),
new TestEdge(E, F, 1), new TestEdge(F, D, 1),
new TestEdge(F, G, 1), new TestEdge(F, H, 1),
new TestEdge(A, E, 3), new TestEdge(B, D, 1)));
KshortestPathSearch<TestVertex, TestEdge> gs = new KshortestPathSearch<TestVertex, TestEdge>(graph);
List<List<TestEdge>> result = gs.search(A, G, weight, 6);
List<Path> paths = new ArrayList<>();
Iterator<List<TestEdge>> itr = result.iterator();
while (itr.hasNext()) {
System.out.println(itr.next().toString());
}
assertEquals("incorrect paths count", 6, result.size());
// assertEquals("printing the paths", outContent.toString());
}
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@Before
public void setUp() throws Exception {
// System.setOut(new PrintStream(outContent));
}
@After
public void tearDown() throws Exception {
// System.setOut(null);
}
}