blob: d945192d38a0b8ece05116552e95ae63db6a7bd6 [file] [log] [blame]
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -07003 *
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
17package org.onlab.graph;
18
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070019import com.google.common.collect.Sets;
20
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070021import java.util.ArrayList;
22import java.util.Set;
23import java.util.List;
24import java.util.Map;
25import java.util.HashMap;
26import java.util.HashSet;
27import java.util.stream.Collectors;
28
29/**
30 * Suurballe shortest-path graph search algorithm capable of finding both
31 * a shortest path, as well as a backup shortest path, between a source and a destination
32 * such that the sum of the path lengths is minimized.
33 */
34public class SuurballeGraphSearch<V extends Vertex, E extends Edge<V>> extends DijkstraGraphSearch<V, E> {
35
36 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +030037 protected Result<V, E> internalSearch(Graph<V, E> graph, V src, V dst,
38 EdgeWeigher<V, E> weigher, int maxPaths) {
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070039 // FIXME: This method needs to be refactored as it is difficult to follow and debug.
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070040
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070041 // FIXME: There is a defect here triggered by 3+ edges between the same vertices,
42 // which makes an attempt to produce looping paths. Protection against
43 // this was added to AbstractGraphPathSearch, but the root issue remains here.
44
45 // FIXME: There is a defect here where not all paths are truly disjoint.
46 // This class needs to filter its own results to make sure that the
47 // paths are indeed disjoint. Temporary fix for this is provided, but
48 // the issue needs to be addressed through refactoring.
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070049
Andrey Komarov2398d962016-09-26 15:11:23 +030050 EdgeWeigher weightf = weigher;
51 DefaultResult firstDijkstraS = (DefaultResult) super.internalSearch(
52 graph, src, dst, weigher, ALL_PATHS);
53 DefaultResult firstDijkstra = (DefaultResult) super.internalSearch(
54 graph, src, null, weigher, ALL_PATHS);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070055
56 //choose an arbitrary shortest path to run Suurballe on
57 Path<V, E> shortPath = null;
Jon Hallcbd1b392017-01-18 20:15:44 -080058 if (firstDijkstraS.paths().isEmpty()) {
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070059 return firstDijkstraS;
60 }
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070061
62 DisjointPathResult result = new DisjointPathResult(firstDijkstra, src, dst, maxPaths);
63
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070064 for (Path p: firstDijkstraS.paths()) {
65 shortPath = p;
66 //transforms the graph so tree edges have 0 weight
Andrey Komarov2398d962016-09-26 15:11:23 +030067 EdgeWeigher<V, E> modified = new EdgeWeigher<V, E>() {
68 @Override
69 public Weight weight(E edge) {
70 return edge instanceof ReverseEdge ?
71 weightf.getInitialWeight() :
72 weightf.weight(edge).merge(firstDijkstra.cost(edge.src()))
73 .subtract(firstDijkstra.cost(edge.dst()));
74 }
75
76 @Override
77 public Weight getInitialWeight() {
78 return weightf.getInitialWeight();
79 }
80
81 @Override
82 public Weight getNonViableWeight() {
83 return weightf.getNonViableWeight();
84 }
85 };
86
87 EdgeWeigher<V, E> modified2 = new EdgeWeigher<V, E>() {
88 @Override
89 public Weight weight(E edge) {
90 return weightf.weight(edge).merge(firstDijkstra.cost(edge.src()))
91 .subtract(firstDijkstra.cost(edge.dst()));
92 }
93
94 @Override
95 public Weight getInitialWeight() {
96 return weightf.getInitialWeight();
97 }
98
99 @Override
100 public Weight getNonViableWeight() {
101 return weightf.getNonViableWeight();
102 }
103 };
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700104
105 //create a residual graph g' by removing all src vertices and reversing 0 length path edges
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700106 MutableGraph<V, E> gt = mutableCopy(graph);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700107
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700108 Map<E, E> revToEdge = new HashMap<>();
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700109 graph.getEdgesTo(src).forEach(gt::removeEdge);
110 for (E edge: shortPath.edges()) {
111 gt.removeEdge(edge);
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700112 Edge<V> reverse = new ReverseEdge<V>(edge);
113 revToEdge.put((E) reverse, edge);
114 gt.addEdge((E) reverse);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700115 }
116
117 //rerun dijkstra on the temporary graph to get a second path
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700118 Result<V, E> secondDijkstra = new DijkstraGraphSearch<V, E>()
119 .search(gt, src, dst, modified, ALL_PATHS);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700120
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700121 Path<V, E> residualShortPath = null;
Jon Hallcbd1b392017-01-18 20:15:44 -0800122 if (secondDijkstra.paths().isEmpty()) {
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700123 result.dpps.add(new DisjointPathPair<>(shortPath, null));
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700124 continue;
125 }
126
127 for (Path p2: secondDijkstra.paths()) {
128 residualShortPath = p2;
129
130 MutableGraph<V, E> roundTrip = mutableCopy(graph);
131
132 List<E> tmp = roundTrip.getEdges().stream().collect(Collectors.toList());
133
134 tmp.forEach(roundTrip::removeEdge);
135
136 shortPath.edges().forEach(roundTrip::addEdge);
137
138 if (residualShortPath != null) {
139 for (Edge<V> edge: residualShortPath.edges()) {
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700140 if (edge instanceof ReverseEdge) {
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700141 roundTrip.removeEdge(revToEdge.get(edge));
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700142 } else {
143 roundTrip.addEdge((E) edge);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700144 }
145 }
146 }
147 //Actually build the final result
Andrey Komarov2398d962016-09-26 15:11:23 +0300148 DefaultResult lastSearch = (DefaultResult)
149 super.internalSearch(roundTrip, src, dst, weigher, ALL_PATHS);
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700150 Path<V, E> primary = lastSearch.paths().iterator().next();
151 primary.edges().forEach(roundTrip::removeEdge);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700152
Andrey Komarov2398d962016-09-26 15:11:23 +0300153 Set<Path<V, E>> backups = super.internalSearch(roundTrip, src, dst,
154 weigher, ALL_PATHS).paths();
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700155
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700156 // Find first backup path that does not share any nodes with the primary
157 for (Path<V, E> backup : backups) {
158 if (isDisjoint(primary, backup)) {
159 result.dpps.add(new DisjointPathPair<>(primary, backup));
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700160 break;
161 }
162 }
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700163 }
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700164 }
165
166 for (int i = result.dpps.size() - 1; i > 0; i--) {
167 if (result.dpps.get(i).size() <= 1) {
168 result.dpps.remove(i);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700169 }
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700170 }
171
172 result.buildPaths();
173 return result;
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700174 }
175
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700176 private boolean isDisjoint(Path<V, E> a, Path<V, E> b) {
177 return Sets.intersection(vertices(a), vertices(b)).isEmpty();
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700178 }
179
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700180 private Set<V> vertices(Path<V, E> p) {
181 Set<V> set = new HashSet<>();
182 p.edges().forEach(e -> set.add(e.src()));
183 set.remove(p.src());
184 return set;
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700185 }
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700186
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700187 /**
188 * Creates a mutable copy of an immutable graph.
189 *
190 * @param graph immutable graph
191 * @return mutable copy
192 */
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700193 private MutableGraph<V, E> mutableCopy(Graph<V, E> graph) {
194 return new MutableAdjacencyListsGraph<>(graph.getVertexes(), graph.getEdges());
195 }
196
197 private static final class ReverseEdge<V extends Vertex> extends AbstractEdge<V> {
198 private ReverseEdge(Edge<V> edge) {
199 super(edge.dst(), edge.src());
200 }
201
202 @Override
203 public String toString() {
204 return "ReversedEdge " + "src=" + src() + " dst=" + dst();
205 }
206 }
207
208 // Auxiliary result for disjoint path search
209 private final class DisjointPathResult implements AbstractGraphPathSearch.Result<V, E> {
210
211 private final Result<V, E> searchResult;
212 private final V src, dst;
213 private final int maxPaths;
214 private final List<DisjointPathPair<V, E>> dpps = new ArrayList<>();
215 private final Set<Path<V, E>> disjointPaths = new HashSet<>();
216
217 private DisjointPathResult(Result<V, E> searchResult, V src, V dst, int maxPaths) {
218 this.searchResult = searchResult;
219 this.src = src;
220 this.dst = dst;
221 this.maxPaths = maxPaths;
222 }
223
224 @Override
225 public V src() {
226 return src;
227 }
228
229 @Override
230 public V dst() {
231 return dst;
232 }
233
234 @Override
235 public Set<Path<V, E>> paths() {
236 return disjointPaths;
237 }
238
239 private void buildPaths() {
240 int paths = 0;
241 for (DisjointPathPair<V, E> path: dpps) {
242 disjointPaths.add(path);
243 paths++;
244 if (paths == maxPaths) {
245 break;
246 }
247 }
248 }
249
250 @Override
251 public Map<V, Set<E>> parents() {
252 return searchResult.parents();
253 }
254
255 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +0300256 public Map<V, Weight> costs() {
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700257 return searchResult.costs();
258 }
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700259 }
260}
261