blob: f517d5a8f8c864575ce9f9b0ce4830f2c6ff83d1 [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
37 public Result<V, E> search(Graph<V, E> graph, V src, V dst,
38 EdgeWeight<V, E> weight, 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 if (weight == null) {
50 weight = edge -> 1;
51 }
52
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070053 final EdgeWeight weightf = weight;
54 DefaultResult firstDijkstraS = (DefaultResult) super.search(graph, src, dst, weight, ALL_PATHS);
55 DefaultResult firstDijkstra = (DefaultResult) super.search(graph, src, null, weight, ALL_PATHS);
56
57 //choose an arbitrary shortest path to run Suurballe on
58 Path<V, E> shortPath = null;
Jon Hallcbd1b392017-01-18 20:15:44 -080059 if (firstDijkstraS.paths().isEmpty()) {
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070060 return firstDijkstraS;
61 }
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070062
63 DisjointPathResult result = new DisjointPathResult(firstDijkstra, src, dst, maxPaths);
64
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070065 for (Path p: firstDijkstraS.paths()) {
66 shortPath = p;
67 //transforms the graph so tree edges have 0 weight
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070068 EdgeWeight<V, E> modified = edge ->
69 edge instanceof ReverseEdge ? 0 :
70 weightf.weight(edge) + firstDijkstra.cost(edge.src()) - firstDijkstra.cost(edge.dst());
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070071 EdgeWeight<V, E> modified2 = edge ->
72 weightf.weight(edge) + firstDijkstra.cost(edge.src()) - firstDijkstra.cost(edge.dst());
73
74 //create a residual graph g' by removing all src vertices and reversing 0 length path edges
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070075 MutableGraph<V, E> gt = mutableCopy(graph);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070076
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070077 Map<E, E> revToEdge = new HashMap<>();
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070078 graph.getEdgesTo(src).forEach(gt::removeEdge);
79 for (E edge: shortPath.edges()) {
80 gt.removeEdge(edge);
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070081 Edge<V> reverse = new ReverseEdge<V>(edge);
82 revToEdge.put((E) reverse, edge);
83 gt.addEdge((E) reverse);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070084 }
85
86 //rerun dijkstra on the temporary graph to get a second path
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070087 Result<V, E> secondDijkstra = new DijkstraGraphSearch<V, E>()
88 .search(gt, src, dst, modified, ALL_PATHS);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070089
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070090 Path<V, E> residualShortPath = null;
Jon Hallcbd1b392017-01-18 20:15:44 -080091 if (secondDijkstra.paths().isEmpty()) {
Thomas Vachuskab1995cb2016-08-23 09:36:08 -070092 result.dpps.add(new DisjointPathPair<>(shortPath, null));
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -070093 continue;
94 }
95
96 for (Path p2: secondDijkstra.paths()) {
97 residualShortPath = p2;
98
99 MutableGraph<V, E> roundTrip = mutableCopy(graph);
100
101 List<E> tmp = roundTrip.getEdges().stream().collect(Collectors.toList());
102
103 tmp.forEach(roundTrip::removeEdge);
104
105 shortPath.edges().forEach(roundTrip::addEdge);
106
107 if (residualShortPath != null) {
108 for (Edge<V> edge: residualShortPath.edges()) {
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700109 if (edge instanceof ReverseEdge) {
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700110 roundTrip.removeEdge(revToEdge.get(edge));
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700111 } else {
112 roundTrip.addEdge((E) edge);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700113 }
114 }
115 }
116 //Actually build the final result
117 DefaultResult lastSearch = (DefaultResult) super.search(roundTrip, src, dst, weight, ALL_PATHS);
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700118 Path<V, E> primary = lastSearch.paths().iterator().next();
119 primary.edges().forEach(roundTrip::removeEdge);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700120
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700121 Set<Path<V, E>> backups = super.search(roundTrip, src, dst, weight, ALL_PATHS).paths();
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700122
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700123 // Find first backup path that does not share any nodes with the primary
124 for (Path<V, E> backup : backups) {
125 if (isDisjoint(primary, backup)) {
126 result.dpps.add(new DisjointPathPair<>(primary, backup));
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700127 break;
128 }
129 }
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700130 }
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700131 }
132
133 for (int i = result.dpps.size() - 1; i > 0; i--) {
134 if (result.dpps.get(i).size() <= 1) {
135 result.dpps.remove(i);
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700136 }
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700137 }
138
139 result.buildPaths();
140 return result;
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700141 }
142
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700143 private boolean isDisjoint(Path<V, E> a, Path<V, E> b) {
144 return Sets.intersection(vertices(a), vertices(b)).isEmpty();
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700145 }
146
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700147 private Set<V> vertices(Path<V, E> p) {
148 Set<V> set = new HashSet<>();
149 p.edges().forEach(e -> set.add(e.src()));
150 set.remove(p.src());
151 return set;
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700152 }
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700153
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700154 /**
155 * Creates a mutable copy of an immutable graph.
156 *
157 * @param graph immutable graph
158 * @return mutable copy
159 */
Thomas Vachuskab1995cb2016-08-23 09:36:08 -0700160 private MutableGraph<V, E> mutableCopy(Graph<V, E> graph) {
161 return new MutableAdjacencyListsGraph<>(graph.getVertexes(), graph.getEdges());
162 }
163
164 private static final class ReverseEdge<V extends Vertex> extends AbstractEdge<V> {
165 private ReverseEdge(Edge<V> edge) {
166 super(edge.dst(), edge.src());
167 }
168
169 @Override
170 public String toString() {
171 return "ReversedEdge " + "src=" + src() + " dst=" + dst();
172 }
173 }
174
175 // Auxiliary result for disjoint path search
176 private final class DisjointPathResult implements AbstractGraphPathSearch.Result<V, E> {
177
178 private final Result<V, E> searchResult;
179 private final V src, dst;
180 private final int maxPaths;
181 private final List<DisjointPathPair<V, E>> dpps = new ArrayList<>();
182 private final Set<Path<V, E>> disjointPaths = new HashSet<>();
183
184 private DisjointPathResult(Result<V, E> searchResult, V src, V dst, int maxPaths) {
185 this.searchResult = searchResult;
186 this.src = src;
187 this.dst = dst;
188 this.maxPaths = maxPaths;
189 }
190
191 @Override
192 public V src() {
193 return src;
194 }
195
196 @Override
197 public V dst() {
198 return dst;
199 }
200
201 @Override
202 public Set<Path<V, E>> paths() {
203 return disjointPaths;
204 }
205
206 private void buildPaths() {
207 int paths = 0;
208 for (DisjointPathPair<V, E> path: dpps) {
209 disjointPaths.add(path);
210 paths++;
211 if (paths == maxPaths) {
212 break;
213 }
214 }
215 }
216
217 @Override
218 public Map<V, Set<E>> parents() {
219 return searchResult.parents();
220 }
221
222 @Override
223 public Map<V, Double> costs() {
224 return searchResult.costs();
225 }
Nikhil Cheerlaf7c2e1a2015-07-09 12:06:37 -0700226 }
227}
228