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