blob: 6e9330e1425bb546f1ce6f8cfff0ab0640fcbb0b [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska24c849c2014-10-27 09:53:05 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska24c849c2014-10-27 09:53:05 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
tome3489412014-08-29 02:30:38 -070016package org.onlab.graph;
17
18import java.util.HashMap;
19import java.util.HashSet;
20import java.util.Iterator;
21import java.util.Map;
22import java.util.Set;
23
24import static com.google.common.base.Preconditions.checkArgument;
25import static com.google.common.base.Preconditions.checkNotNull;
26
27/**
28 * Basis for various graph path search algorithm implementations.
29 *
30 * @param <V> vertex type
31 * @param <E> edge type
32 */
tom144de692014-08-29 11:38:44 -070033public abstract class AbstractGraphPathSearch<V extends Vertex, E extends Edge<V>>
tome3489412014-08-29 02:30:38 -070034 implements GraphPathSearch<V, E> {
35
Thomas Vachuska4353a5a2014-10-27 15:18:10 -070036 private double samenessThreshold = Double.MIN_VALUE;
tome3489412014-08-29 02:30:38 -070037
38 /**
39 * Sets a new sameness threshold for comparing cost values; default is
40 * is {@code 0.000000001}.
41 *
42 * @param threshold fractional double value
43 */
44 public void setSamenessThreshold(double threshold) {
45 samenessThreshold = threshold;
46 }
47
48 /**
49 * Returns the current sameness threshold for comparing cost values.
50 *
51 * @return current threshold
52 */
53 public double samenessThreshold() {
54 return samenessThreshold;
55 }
56
57 /**
58 * Default path search result that uses the DefaultPath to convey paths
59 * in a graph.
60 */
61 protected class DefaultResult implements Result<V, E> {
62
63 private final V src;
64 private final V dst;
65 protected final Set<Path<V, E>> paths = new HashSet<>();
66 protected final Map<V, Double> costs = new HashMap<>();
67 protected final Map<V, Set<E>> parents = new HashMap<>();
68
69 /**
70 * Creates the result of path search.
71 *
72 * @param src path source
73 * @param dst optional path destination
74 */
75 public DefaultResult(V src, V dst) {
76 checkNotNull(src, "Source cannot be null");
77 this.src = src;
78 this.dst = dst;
79 }
80
81 @Override
82 public V src() {
83 return src;
84 }
85
86 @Override
87 public V dst() {
88 return dst;
89 }
90
91 @Override
92 public Set<Path<V, E>> paths() {
93 return paths;
94 }
95
96 @Override
97 public Map<V, Double> costs() {
98 return costs;
99 }
100
101 @Override
102 public Map<V, Set<E>> parents() {
103 return parents;
104 }
105
106 /**
107 * Indicates whether or not the given vertex has a cost yet.
108 *
109 * @param v vertex to test
110 * @return true if the vertex has cost already
111 */
112 boolean hasCost(V v) {
113 return costs.get(v) != null;
114 }
115
116 /**
117 * Returns the current cost to reach the specified vertex.
118 *
119 * @return cost to reach the vertex
120 */
121 double cost(V v) {
122 Double c = costs.get(v);
123 return c == null ? Double.MAX_VALUE : c;
124 }
125
126 /**
127 * Updates the cost of the vertex using its existing cost plus the
128 * cost to traverse the specified edge.
129 *
130 * @param v vertex
131 * @param edge edge through which vertex is reached
132 * @param cost current cost to reach the vertex from the source
133 * @param replace true to indicate that any accrued edges are to be
134 * cleared; false to indicate that the edge should be
135 * added to the previously accrued edges as they yield
136 * the same cost
137 */
138 void updateVertex(V v, E edge, double cost, boolean replace) {
139 costs.put(v, cost);
140 if (edge != null) {
141 Set<E> edges = parents.get(v);
142 if (edges == null) {
143 edges = new HashSet<>();
144 parents.put(v, edges);
145 }
146 if (replace) {
147 edges.clear();
148 }
149 edges.add(edge);
150 }
151 }
152
153 /**
154 * Removes the set of parent edges for the specified vertex.
155 *
156 * @param v vertex
157 */
158 void removeVertex(V v) {
159 parents.remove(v);
160 }
161
162 /**
163 * If possible, relax the specified edge using the supplied base cost
164 * and edge-weight function.
165 *
Thomas Vachuska4d690872014-10-27 08:57:08 -0700166 * @param e edge to be relaxed
167 * @param cost base cost to reach the edge destination vertex
168 * @param ew optional edge weight function
169 * @param forbidNegatives if true negative values will forbid the link
tome3489412014-08-29 02:30:38 -0700170 * @return true if the edge was relaxed; false otherwise
171 */
Thomas Vachuska4d690872014-10-27 08:57:08 -0700172 boolean relaxEdge(E e, double cost, EdgeWeight<V, E> ew,
173 boolean... forbidNegatives) {
tome3489412014-08-29 02:30:38 -0700174 V v = e.dst();
175 double oldCost = cost(v);
Thomas Vachuska4d690872014-10-27 08:57:08 -0700176 double hopCost = ew == null ? 1.0 : ew.weight(e);
177 if (hopCost < 0 && forbidNegatives.length == 1 && forbidNegatives[0]) {
178 return false;
179 }
180
181 double newCost = cost + hopCost;
tome3489412014-08-29 02:30:38 -0700182 boolean relaxed = newCost < oldCost;
Thomas Vachuska4353a5a2014-10-27 15:18:10 -0700183 boolean same = Math.abs(newCost - oldCost) <= samenessThreshold;
tome3489412014-08-29 02:30:38 -0700184 if (same || relaxed) {
185 updateVertex(v, e, newCost, !same);
186 }
187 return relaxed;
188 }
189
190 /**
191 * Builds a set of paths for the specified src/dst vertex pair.
192 */
193 protected void buildPaths() {
194 Set<V> destinations = new HashSet<>();
195 if (dst == null) {
196 destinations.addAll(costs.keySet());
197 } else {
198 destinations.add(dst);
199 }
200
201 // Build all paths between the source and all requested destinations.
202 for (V v : destinations) {
203 // Ignore the source, if it is among the destinations.
204 if (!v.equals(src)) {
205 buildAllPaths(this, src, v);
206 }
207 }
208 }
209
210 }
211
212 /**
213 * Builds a set of all paths between the source and destination using the
214 * graph search result by applying breadth-first search through the parent
215 * edges and vertex costs.
216 *
217 * @param result graph search result
218 * @param src source vertex
219 * @param dst destination vertex
220 */
221 private void buildAllPaths(DefaultResult result, V src, V dst) {
222 DefaultMutablePath<V, E> basePath = new DefaultMutablePath<>();
223 basePath.setCost(result.cost(dst));
224
225 Set<DefaultMutablePath<V, E>> pendingPaths = new HashSet<>();
226 pendingPaths.add(basePath);
227
228 while (!pendingPaths.isEmpty()) {
229 Set<DefaultMutablePath<V, E>> frontier = new HashSet<>();
230
231 for (DefaultMutablePath<V, E> path : pendingPaths) {
232 // For each pending path, locate its first vertex since we
233 // will be moving backwards from it.
234 V firstVertex = firstVertex(path, dst);
235
236 // If the first vertex is our expected source, we have reached
237 // the beginning, so add the this path to the result paths.
238 if (firstVertex.equals(src)) {
239 path.setCost(result.cost(dst));
240 result.paths.add(new DefaultPath<>(path.edges(), path.cost()));
241
242 } else {
243 // If we have not reached the beginning, i.e. the source,
244 // fetch the set of edges leading to the first vertex of
245 // this pending path; if there are none, abandon processing
246 // this path for good.
247 Set<E> firstVertexParents = result.parents.get(firstVertex);
248 if (firstVertexParents == null || firstVertexParents.isEmpty()) {
249 break;
250 }
251
252 // Now iterate over all the edges and for each of them
253 // cloning the current path and then insert that edge to
254 // the path and then add that path to the pending ones.
255 // When processing the last edge, modify the current
256 // pending path rather than cloning a new one.
257 Iterator<E> edges = firstVertexParents.iterator();
258 while (edges.hasNext()) {
259 E edge = edges.next();
260 boolean isLast = !edges.hasNext();
261 DefaultMutablePath<V, E> pendingPath = isLast ? path : new DefaultMutablePath<>(path);
262 pendingPath.insertEdge(edge);
263 frontier.add(pendingPath);
264 }
265 }
266 }
267
268 // All pending paths have been scanned so promote the next frontier
269 pendingPaths = frontier;
270 }
271 }
272
273 // Returns the first vertex of the specified path. This is either the source
274 // of the first edge or, if there are no edges yet, the given destination.
275 private V firstVertex(Path<V, E> path, V dst) {
276 return path.edges().isEmpty() ? dst : path.edges().get(0).src();
277 }
278
279 /**
280 * Checks the specified path search arguments for validity.
281 *
282 * @param graph graph; must not be null
283 * @param src source vertex; must not be null and belong to graph
284 * @param dst optional target vertex; must belong to graph
285 */
286 protected void checkArguments(Graph<V, E> graph, V src, V dst) {
287 checkNotNull(graph, "Graph cannot be null");
288 checkNotNull(src, "Source cannot be null");
289 Set<V> vertices = graph.getVertexes();
290 checkArgument(vertices.contains(src), "Source not in the graph");
291 checkArgument(dst == null || vertices.contains(dst),
292 "Destination not in graph");
293 }
294
295}