blob: f097b6b2deed9cf2a5ec5105508857d09ae57030 [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
19import java.util.ArrayList;
20import java.util.Set;
21import java.util.List;
22import java.util.Map;
23import java.util.HashMap;
24import java.util.HashSet;
25import java.util.stream.Collectors;
26
27/**
28 * Suurballe shortest-path graph search algorithm capable of finding both
29 * a shortest path, as well as a backup shortest path, between a source and a destination
30 * such that the sum of the path lengths is minimized.
31 */
32public class SuurballeGraphSearch<V extends Vertex, E extends Edge<V>> extends DijkstraGraphSearch<V, E> {
33
34 @Override
35 public Result<V, E> search(Graph<V, E> graph, V src, V dst,
36 EdgeWeight<V, E> weight, int maxPaths) {
37
38 if (weight == null) {
39 weight = edge -> 1;
40 }
41
42 List<DisjointPathPair<V, E>> dpps = new ArrayList<>();
43
44 final EdgeWeight weightf = weight;
45 DefaultResult firstDijkstraS = (DefaultResult) super.search(graph, src, dst, weight, ALL_PATHS);
46 DefaultResult firstDijkstra = (DefaultResult) super.search(graph, src, null, weight, ALL_PATHS);
47
48 //choose an arbitrary shortest path to run Suurballe on
49 Path<V, E> shortPath = null;
50 if (firstDijkstraS.paths().size() == 0) {
51 return firstDijkstraS;
52 }
53 for (Path p: firstDijkstraS.paths()) {
54 shortPath = p;
55 //transforms the graph so tree edges have 0 weight
56 EdgeWeight<V, Edge<V>> modified = edge -> {
57 if (classE().isInstance(edge)) {
58 return weightf.weight((E) (edge)) + firstDijkstra.cost(edge.src())
59 - firstDijkstra.cost(edge.dst());
60 }
61 return 0;
62 };
63 EdgeWeight<V, E> modified2 = edge ->
64 weightf.weight(edge) + firstDijkstra.cost(edge.src()) - firstDijkstra.cost(edge.dst());
65
66 //create a residual graph g' by removing all src vertices and reversing 0 length path edges
67 MutableGraph<V, Edge<V>> gt = mutableCopy(graph);
68
69 Map<Edge<V>, E> revToEdge = new HashMap<>();
70 graph.getEdgesTo(src).forEach(gt::removeEdge);
71 for (E edge: shortPath.edges()) {
72 gt.removeEdge(edge);
73 Edge<V> reverse = new Edge<V>() {
74 final Edge<V> orig = edge;
75 public V src() {
76 return orig.dst();
77 }
78 public V dst() {
79 return orig.src();
80 }
81 public String toString() {
82 return "ReversedEdge " + "src=" + src() + " dst=" + dst();
83 }
84 };
85 revToEdge.put(reverse, edge);
86 gt.addEdge(reverse);
87 }
88
89 //rerun dijkstra on the temporary graph to get a second path
90 Result<V, Edge<V>> secondDijkstra;
91 secondDijkstra = new DijkstraGraphSearch<V, Edge<V>>().search(gt, src, dst, modified, ALL_PATHS);
92
93 Path<V, Edge<V>> residualShortPath = null;
94 if (secondDijkstra.paths().size() == 0) {
95 dpps.add(new DisjointPathPair<V, E>(shortPath, null));
96 continue;
97 }
98
99 for (Path p2: secondDijkstra.paths()) {
100 residualShortPath = p2;
101
102 MutableGraph<V, E> roundTrip = mutableCopy(graph);
103
104 List<E> tmp = roundTrip.getEdges().stream().collect(Collectors.toList());
105
106 tmp.forEach(roundTrip::removeEdge);
107
108 shortPath.edges().forEach(roundTrip::addEdge);
109
110 if (residualShortPath != null) {
111 for (Edge<V> edge: residualShortPath.edges()) {
112 if (classE().isInstance(edge)) {
113 roundTrip.addEdge((E) edge);
114 } else {
115 roundTrip.removeEdge(revToEdge.get(edge));
116 }
117 }
118 }
119 //Actually build the final result
120 DefaultResult lastSearch = (DefaultResult) super.search(roundTrip, src, dst, weight, ALL_PATHS);
121 Path<V, E> path1 = lastSearch.paths().iterator().next();
122 path1.edges().forEach(roundTrip::removeEdge);
123
124 Set<Path<V, E>> bckpaths = super.search(roundTrip, src, dst, weight, ALL_PATHS).paths();
125 Path<V, E> backup = null;
126 if (bckpaths.size() != 0) {
127 backup = bckpaths.iterator().next();
128 }
129
130 dpps.add(new DisjointPathPair<>(path1, backup));
131 }
132 }
133
134 for (int i = dpps.size() - 1; i > 0; i--) {
135 if (dpps.get(i).size() <= 1) {
136 dpps.remove(i);
137 }
138 }
139
140 return new Result<V, E>() {
141 final DefaultResult search = firstDijkstra;
142
143 public V src() {
144 return src;
145 }
146 public V dst() {
147 return dst;
148 }
149 public Set<Path<V, E>> paths() {
150 Set<Path<V, E>> pathsD = new HashSet<>();
151 int paths = 0;
152 for (DisjointPathPair<V, E> path: dpps) {
153 pathsD.add((Path<V, E>) path);
154 paths++;
155 if (paths == maxPaths) {
156 break;
157 }
158 }
159 return pathsD;
160 }
161 public Map<V, Double> costs() {
162 return search.costs();
163 }
164 public Map<V, Set<E>> parents() {
165 return search.parents();
166 }
167 };
168 }
169
170 private Class<?> clazzV;
171
172 public Class<?> classV() {
173 return clazzV;
174 }
175
176 private Class<?> clazzE;
177
178 public Class<?> classE() {
179 return clazzE;
180 }
181 /**
182 * Creates a mutable copy of an immutable graph.
183 *
184 * @param graph immutable graph
185 * @return mutable copy
186 */
187 public MutableGraph mutableCopy(Graph<V, E> graph) {
188 clazzV = graph.getVertexes().iterator().next().getClass();
189 clazzE = graph.getEdges().iterator().next().getClass();
190 return new MutableAdjacencyListsGraph<V, E>(graph.getVertexes(), graph.getEdges());
191 }
192}
193