blob: 00bda43da6fc94bcde31f05018ecf17caf7e3704 [file] [log] [blame]
Aaron Kruglikov07d80382015-12-07 16:54:37 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Aaron Kruglikov07d80382015-12-07 16:54:37 -08003 *
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 */
16package org.onlab.graph;
17
18import com.google.common.collect.ImmutableSet;
19import com.google.common.collect.Lists;
20import com.google.common.collect.Sets;
21import org.slf4j.Logger;
22
23import java.util.ArrayList;
24import java.util.Comparator;
25import java.util.List;
26import java.util.Set;
27import java.util.TreeSet;
28
29import static com.google.common.base.Preconditions.checkArgument;
30import static com.google.common.base.Preconditions.checkNotNull;
31import 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 */
38public class KShortestPathsSearch<V extends Vertex, E extends Edge<V>> extends AbstractGraphPathSearch<V, E> {
39
40 private final Logger log = getLogger(getClass());
41
42 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +030043 protected Result<V, E> internalSearch(Graph<V, E> graph, V src, V dst, EdgeWeigher<V, E> weigher, int maxPaths) {
44 //The modified edge weigher removes any need to modify the original graph
45 InnerEdgeWeigher modifiedWeighter = new InnerEdgeWeigher(checkNotNull(weigher));
Aaron Kruglikovf6aed992016-11-08 15:16:03 -080046 checkArgument(maxPaths != ALL_PATHS, "KShortestPath search cannot" +
47 "be used with ALL_PATHS.");
48 checkArgument(maxPaths > 0, "The max number of paths must be greater" +
49 " than 0");
Aaron Kruglikov07d80382015-12-07 16:54:37 -080050 Graph<V, E> originalGraph = checkNotNull(graph);
51 //the result contains the set of eventual results
52 InnerOrderedResult result = new InnerOrderedResult(src, dst, maxPaths);
53 ArrayList<Path<V, E>> resultPaths = new ArrayList<>(maxPaths);
54 ArrayList<Path<V, E>> potentialPaths = Lists.newArrayList();
55
56 DijkstraGraphSearch<V, E> dijkstraSearch = new DijkstraGraphSearch<>();
57 Set<Path<V, E>> dijkstraResults = dijkstraSearch.search(originalGraph, src, dst, modifiedWeighter, 1).paths();
58 //Checks if the dst was reachable
Jon Hallcbd1b392017-01-18 20:15:44 -080059 if (dijkstraResults.isEmpty()) {
Aaron Kruglikov07d80382015-12-07 16:54:37 -080060 log.warn("No path was found.");
61 return result;
62 }
63 //If it was reachable adds the first shortest path to the set of results
64 resultPaths.add(dijkstraResults.iterator().next());
65
66 for (int k = 1; k < maxPaths; k++) {
67
68 for (int i = 0; i < (resultPaths.get(k - 1).edges().size() - 1); i++) {
69 V spurNode = resultPaths.get(k - 1).edges().get(i).src();
70 List<E> rootPathEdgeList = resultPaths.get(k - 1).edges().subList(0, i);
71
72 for (Path<V, E> path : resultPaths) {
Koosha1525c452016-10-28 18:25:19 +033073 if (path.edges().size() >= i && edgeListsAreEqual(rootPathEdgeList, path.edges().subList(0, i))) {
Aaron Kruglikov07d80382015-12-07 16:54:37 -080074 modifiedWeighter.removedEdges.add(path.edges().get(i));
75 }
76 }
77
78 //Effectively remove all nodes from the source path
79 for (E edge : rootPathEdgeList) {
80 originalGraph.getEdgesFrom(edge.src()).forEach(e -> modifiedWeighter.removedEdges.add(e));
81 originalGraph.getEdgesTo(edge.src()).forEach(e -> modifiedWeighter.removedEdges.add(e));
82 }
83
84 dijkstraResults = dijkstraSearch.search(originalGraph, spurNode, dst, modifiedWeighter, 1).paths();
Jon Hallcbd1b392017-01-18 20:15:44 -080085 if (!dijkstraResults.isEmpty()) {
Aaron Kruglikov07d80382015-12-07 16:54:37 -080086 Path<V, E> spurPath = dijkstraResults.iterator().next();
87 List<E> totalPath = new ArrayList<>(rootPathEdgeList);
Andrey Komarov2398d962016-09-26 15:11:23 +030088 spurPath.edges().forEach(totalPath::add);
89 //The following line must use the original weigher not the modified weigher because the modified
90 //weigher will count -1 values used for modifying the graph and return an inaccurate cost.
Aaron Kruglikov07d80382015-12-07 16:54:37 -080091 potentialPaths.add(new DefaultPath<V, E>(totalPath,
Andrey Komarov2398d962016-09-26 15:11:23 +030092 calculatePathCost(weigher, totalPath)));
Aaron Kruglikov07d80382015-12-07 16:54:37 -080093 }
94
95 //Restore all removed paths and nodes
96 modifiedWeighter.removedEdges.clear();
97 }
98 if (potentialPaths.isEmpty()) {
99 break;
100 }
101 potentialPaths.sort(new InnerPathComparator());
102 resultPaths.add(potentialPaths.get(0));
103 potentialPaths.remove(0);
104 }
105 result.pathSet.addAll(resultPaths);
106
107 return result;
108 }
109 //Edge list equality is judges by shared endpoints, and shared endpoints should be the same
110 private boolean edgeListsAreEqual(List<E> edgeListOne, List<E> edgeListTwo) {
111 if (edgeListOne.size() != edgeListTwo.size()) {
112 return false;
113 }
114 E edgeOne;
115 E edgeTwo;
116 for (int i = 0; i < edgeListOne.size(); i++) {
117 edgeOne = edgeListOne.get(i);
118 edgeTwo = edgeListTwo.get(i);
119 if (!edgeOne.equals(edgeTwo)) {
120 return false;
121 }
122 }
123 return true;
124 }
125
Andrey Komarov2398d962016-09-26 15:11:23 +0300126 private Weight calculatePathCost(EdgeWeigher<V, E> weighter, List<E> edges) {
127 Weight totalCost = weighter.getInitialWeight();
Aaron Kruglikov07d80382015-12-07 16:54:37 -0800128 for (E edge : edges) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300129 totalCost = totalCost.merge(weighter.weight(edge));
Aaron Kruglikov07d80382015-12-07 16:54:37 -0800130 }
131 return totalCost;
132 }
133
134 /**
135 * Weights edges to make them inaccessible if set, otherwise returns the result of the original EdgeWeight.
136 */
Andrey Komarov2398d962016-09-26 15:11:23 +0300137 private final class InnerEdgeWeigher implements EdgeWeigher<V, E> {
Aaron Kruglikov07d80382015-12-07 16:54:37 -0800138
139 private Set<E> removedEdges = Sets.newConcurrentHashSet();
Andrey Komarov2398d962016-09-26 15:11:23 +0300140 private EdgeWeigher<V, E> innerEdgeWeigher;
Aaron Kruglikov07d80382015-12-07 16:54:37 -0800141
Andrey Komarov2398d962016-09-26 15:11:23 +0300142 private InnerEdgeWeigher(EdgeWeigher<V, E> weigher) {
143 this.innerEdgeWeigher = weigher;
Aaron Kruglikov07d80382015-12-07 16:54:37 -0800144 }
145
146 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +0300147 public Weight weight(E edge) {
Aaron Kruglikov07d80382015-12-07 16:54:37 -0800148 if (removedEdges.contains(edge)) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300149 return innerEdgeWeigher.getNonViableWeight();
Aaron Kruglikov07d80382015-12-07 16:54:37 -0800150 }
Andrey Komarov2398d962016-09-26 15:11:23 +0300151 return innerEdgeWeigher.weight(edge);
152 }
153
154 @Override
155 public Weight getInitialWeight() {
156 return innerEdgeWeigher.getInitialWeight();
157 }
158
159 @Override
160 public Weight getNonViableWeight() {
161 return innerEdgeWeigher.getNonViableWeight();
Aaron Kruglikov07d80382015-12-07 16:54:37 -0800162 }
163 }
164
165 /**
166 * A result modified to return paths ordered according to the provided comparator.
167 */
168 protected class InnerOrderedResult extends DefaultResult {
169
170 private TreeSet<Path<V, E>> pathSet = new TreeSet<>(new InnerPathComparator());
171
172 public InnerOrderedResult(V src, V dst) {
173 super(src, dst);
174 }
175
176 public InnerOrderedResult(V src, V dst, int maxPaths) {
177 super(src, dst, maxPaths);
178 }
179
180 @Override
181 public Set<Path<V, E>> paths() {
182 return ImmutableSet.copyOf(pathSet);
183 }
184 }
185
186 /**
187 * Provides a comparator to order the set of paths.
188 */
189 private class InnerPathComparator implements Comparator<Path<V, E>> {
190
191 @Override
192 public int compare(Path<V, E> pathOne, Path<V, E> pathTwo) {
Andrey Komarov2398d962016-09-26 15:11:23 +0300193 int comparisonValue = pathOne.cost().compareTo(pathTwo.cost());
Aaron Kruglikov07d80382015-12-07 16:54:37 -0800194 if (comparisonValue != 0) {
195 return comparisonValue;
196 } else if (edgeListsAreEqual(pathOne.edges(), pathTwo.edges())) {
197 return 0;
198 } else {
199 return 1;
200 }
201 }
202 }
203}