alshabib

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 39 changed files with 2286 additions and 4 deletions
......@@ -45,7 +45,7 @@ public class AbstractListenerRegistry<E extends Event, L extends EventListener<E
for (L listener : listeners) {
try {
listener.event(event);
} catch (Throwable error) {
} catch (Exception error) {
reportProblem(event, error);
}
}
......
......@@ -82,7 +82,7 @@ public class SimpleEventDispatcher extends DefaultEventSinkRegistry
log.warn("No sink registered for event class {}",
event.getClass());
}
} catch (Throwable e) {
} catch (Exception e) {
log.warn("Error encountered while dispatching event:", e);
}
}
......
......@@ -141,7 +141,6 @@ public class SimpleDeviceManager
public void updatePorts(DeviceId deviceId, List<PortDescription> portDescriptions) {
checkNotNull(deviceId, DEVICE_ID_NULL);
checkNotNull(portDescriptions, "Port descriptions list cannot be null");
// FIXME: fix the interface to accept DeviceId separately
log.info("Device {} ports updated: {}", portDescriptions);
List<DeviceEvent> events = store.updatePorts(deviceId, portDescriptions);
for (DeviceEvent event : events) {
......
......@@ -306,7 +306,7 @@
<group>
<title>Utilities</title>
<packages>
org.onlab.util:org.onlab.util.*
org.onlab.*
</packages>
</group>
<group>
......
......@@ -17,6 +17,12 @@
<description>Miscellaneous ON.Lab utilities</description>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-testlib</artifactId>
<version>17.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
......
package org.onlab.graph;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstract graph edge implementation.
*/
public abstract class AbstractEdge<V extends Vertex> implements Edge<V> {
private final V src;
private final V dst;
/**
* Creates a new edge between the specified source and destination vertexes.
*
* @param src source vertex
* @param dst destination vertex
*/
public AbstractEdge(V src, V dst) {
this.src = checkNotNull(src, "Source vertex cannot be null");
this.dst = checkNotNull(dst, "Destination vertex cannot be null");
}
@Override
public V src() {
return src;
}
@Override
public V dst() {
return dst;
}
@Override
public int hashCode() {
return Objects.hash(src, dst);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AbstractEdge) {
final AbstractEdge other = (AbstractEdge) obj;
return Objects.equals(this.src, other.src) && Objects.equals(this.dst, other.dst);
}
return false;
}
@Override
public String toString() {
return com.google.common.base.Objects.toStringHelper(this)
.add("src", src)
.add("dst", dst)
.toString();
}
}
package org.onlab.graph;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Basis for various graph path search algorithm implementations.
*
* @param <V> vertex type
* @param <E> edge type
*/
public abstract class AbstractGraphPathSearch<V extends Vertex, E extends Edge<V>>
implements GraphPathSearch<V, E> {
private double samenessThreshold = 0.000000001;
/**
* Sets a new sameness threshold for comparing cost values; default is
* is {@code 0.000000001}.
*
* @param threshold fractional double value
*/
public void setSamenessThreshold(double threshold) {
samenessThreshold = threshold;
}
/**
* Returns the current sameness threshold for comparing cost values.
*
* @return current threshold
*/
public double samenessThreshold() {
return samenessThreshold;
}
/**
* Default path search result that uses the DefaultPath to convey paths
* in a graph.
*/
protected class DefaultResult implements Result<V, E> {
private final V src;
private final V dst;
protected final Set<Path<V, E>> paths = new HashSet<>();
protected final Map<V, Double> costs = new HashMap<>();
protected final Map<V, Set<E>> parents = new HashMap<>();
/**
* Creates the result of path search.
*
* @param src path source
* @param dst optional path destination
*/
public DefaultResult(V src, V dst) {
checkNotNull(src, "Source cannot be null");
this.src = src;
this.dst = dst;
}
@Override
public V src() {
return src;
}
@Override
public V dst() {
return dst;
}
@Override
public Set<Path<V, E>> paths() {
return paths;
}
@Override
public Map<V, Double> costs() {
return costs;
}
@Override
public Map<V, Set<E>> parents() {
return parents;
}
/**
* Indicates whether or not the given vertex has a cost yet.
*
* @param v vertex to test
* @return true if the vertex has cost already
*/
boolean hasCost(V v) {
return costs.get(v) != null;
}
/**
* Returns the current cost to reach the specified vertex.
*
* @return cost to reach the vertex
*/
double cost(V v) {
Double c = costs.get(v);
return c == null ? Double.MAX_VALUE : c;
}
/**
* Updates the cost of the vertex using its existing cost plus the
* cost to traverse the specified edge.
*
* @param v vertex
* @param edge edge through which vertex is reached
* @param cost current cost to reach the vertex from the source
* @param replace true to indicate that any accrued edges are to be
* cleared; false to indicate that the edge should be
* added to the previously accrued edges as they yield
* the same cost
*/
void updateVertex(V v, E edge, double cost, boolean replace) {
costs.put(v, cost);
if (edge != null) {
Set<E> edges = parents.get(v);
if (edges == null) {
edges = new HashSet<>();
parents.put(v, edges);
}
if (replace) {
edges.clear();
}
edges.add(edge);
}
}
/**
* Removes the set of parent edges for the specified vertex.
*
* @param v vertex
*/
void removeVertex(V v) {
parents.remove(v);
}
/**
* If possible, relax the specified edge using the supplied base cost
* and edge-weight function.
*
* @param e edge to be relaxed
* @param cost base cost to reach the edge destination vertex
* @param ew optional edge weight function
* @return true if the edge was relaxed; false otherwise
*/
boolean relaxEdge(E e, double cost, EdgeWeight<V, E> ew) {
V v = e.dst();
double oldCost = cost(v);
double newCost = cost + (ew == null ? 1.0 : ew.weight(e));
boolean relaxed = newCost < oldCost;
boolean same = Math.abs(newCost - oldCost) < samenessThreshold;
if (same || relaxed) {
updateVertex(v, e, newCost, !same);
}
return relaxed;
}
/**
* Builds a set of paths for the specified src/dst vertex pair.
*/
protected void buildPaths() {
Set<V> destinations = new HashSet<>();
if (dst == null) {
destinations.addAll(costs.keySet());
} else {
destinations.add(dst);
}
// Build all paths between the source and all requested destinations.
for (V v : destinations) {
// Ignore the source, if it is among the destinations.
if (!v.equals(src)) {
buildAllPaths(this, src, v);
}
}
}
}
/**
* Builds a set of all paths between the source and destination using the
* graph search result by applying breadth-first search through the parent
* edges and vertex costs.
*
* @param result graph search result
* @param src source vertex
* @param dst destination vertex
*/
private void buildAllPaths(DefaultResult result, V src, V dst) {
DefaultMutablePath<V, E> basePath = new DefaultMutablePath<>();
basePath.setCost(result.cost(dst));
Set<DefaultMutablePath<V, E>> pendingPaths = new HashSet<>();
pendingPaths.add(basePath);
while (!pendingPaths.isEmpty()) {
Set<DefaultMutablePath<V, E>> frontier = new HashSet<>();
for (DefaultMutablePath<V, E> path : pendingPaths) {
// For each pending path, locate its first vertex since we
// will be moving backwards from it.
V firstVertex = firstVertex(path, dst);
// If the first vertex is our expected source, we have reached
// the beginning, so add the this path to the result paths.
if (firstVertex.equals(src)) {
path.setCost(result.cost(dst));
result.paths.add(new DefaultPath<>(path.edges(), path.cost()));
} else {
// If we have not reached the beginning, i.e. the source,
// fetch the set of edges leading to the first vertex of
// this pending path; if there are none, abandon processing
// this path for good.
Set<E> firstVertexParents = result.parents.get(firstVertex);
if (firstVertexParents == null || firstVertexParents.isEmpty()) {
break;
}
// Now iterate over all the edges and for each of them
// cloning the current path and then insert that edge to
// the path and then add that path to the pending ones.
// When processing the last edge, modify the current
// pending path rather than cloning a new one.
Iterator<E> edges = firstVertexParents.iterator();
while (edges.hasNext()) {
E edge = edges.next();
boolean isLast = !edges.hasNext();
DefaultMutablePath<V, E> pendingPath = isLast ? path : new DefaultMutablePath<>(path);
pendingPath.insertEdge(edge);
frontier.add(pendingPath);
}
}
}
// All pending paths have been scanned so promote the next frontier
pendingPaths = frontier;
}
}
// Returns the first vertex of the specified path. This is either the source
// of the first edge or, if there are no edges yet, the given destination.
private V firstVertex(Path<V, E> path, V dst) {
return path.edges().isEmpty() ? dst : path.edges().get(0).src();
}
/**
* Checks the specified path search arguments for validity.
*
* @param graph graph; must not be null
* @param src source vertex; must not be null and belong to graph
* @param dst optional target vertex; must belong to graph
*/
protected void checkArguments(Graph<V, E> graph, V src, V dst) {
checkNotNull(graph, "Graph cannot be null");
checkNotNull(src, "Source cannot be null");
Set<V> vertices = graph.getVertexes();
checkArgument(vertices.contains(src), "Source not in the graph");
checkArgument(dst == null || vertices.contains(dst),
"Destination not in graph");
}
}
package org.onlab.graph;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import java.util.Objects;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Immutable graph implemented using adjacency lists.
*
* @param <V> vertex type
* @param <E> edge type
*/
public class AdjacencyListsGraph<V extends Vertex, E extends Edge<V>> implements Graph<V, E> {
private final Set<V> vertexes;
private final Set<E> edges;
private final ImmutableSetMultimap<V, E> sources;
private final ImmutableSetMultimap<V, E> destinations;
/**
* Creates a graph comprising of the specified vertexes and edges.
*
* @param vertexes set of graph vertexes
* @param edges set of graph edges
*/
public AdjacencyListsGraph(Set<V> vertexes, Set<E> edges) {
checkNotNull(vertexes, "Vertex set cannot be null");
checkNotNull(edges, "Edge set cannot be null");
// Record ingress/egress edges for each vertex.
ImmutableSetMultimap.Builder<V, E> srcMap = ImmutableSetMultimap.builder();
ImmutableSetMultimap.Builder<V, E> dstMap = ImmutableSetMultimap.builder();
// Also make sure that all edge end-points are added as vertexes
ImmutableSet.Builder<V> actualVertexes = ImmutableSet.builder();
actualVertexes.addAll(vertexes);
for (E edge : edges) {
srcMap.put(edge.src(), edge);
actualVertexes.add(edge.src());
dstMap.put(edge.dst(), edge);
actualVertexes.add(edge.dst());
}
// Make an immutable copy of the edge and vertex sets
this.edges = ImmutableSet.copyOf(edges);
this.vertexes = actualVertexes.build();
// Build immutable copies of sources and destinations edge maps
sources = srcMap.build();
destinations = dstMap.build();
}
@Override
public Set<V> getVertexes() {
return vertexes;
}
@Override
public Set<E> getEdges() {
return edges;
}
@Override
public Set<E> getEdgesFrom(V src) {
return sources.get(src);
}
@Override
public Set<E> getEdgesTo(V dst) {
return destinations.get(dst);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AdjacencyListsGraph) {
AdjacencyListsGraph that = (AdjacencyListsGraph) obj;
return this.getClass() == that.getClass() &&
Objects.equals(this.vertexes, that.vertexes) &&
Objects.equals(this.edges, that.edges);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(vertexes, edges);
}
@Override
public String toString() {
return com.google.common.base.Objects.toStringHelper(this)
.add("vertexes", vertexes)
.add("edges", edges)
.toString();
}
}
package org.onlab.graph;
/**
* Bellman-Ford graph search algorithm for locating shortest-paths in
* directed graphs that may contain negative cycles.
*/
public class BellmanFordGraphSearch<V extends Vertex, E extends Edge<V>>
extends AbstractGraphPathSearch<V, E> {
@Override
public Result<V, E> search(Graph<V, E> graph, V src, V dst,
EdgeWeight<V, E> weight) {
checkArguments(graph, src, dst);
// Prepare the graph search result.
DefaultResult result = new DefaultResult(src, dst);
// The source vertex has cost 0, of course.
result.updateVertex(src, null, 0.0, true);
int max = graph.getVertexes().size() - 1;
for (int i = 0; i < max; i++) {
// Relax, if possible, all egress edges of the current vertex.
for (E edge : graph.getEdges()) {
if (result.hasCost(edge.src())) {
result.relaxEdge(edge, result.cost(edge.src()), weight);
}
}
}
// Remove any vertexes reached by traversing edges with negative weights.
for (E edge : graph.getEdges()) {
if (result.hasCost(edge.src())) {
if (result.relaxEdge(edge, result.cost(edge.src()), weight)) {
result.removeVertex(edge.dst());
}
}
}
// Finally, but the paths on the search result and return.
result.buildPaths();
return result;
}
}
package org.onlab.graph;
import java.util.HashSet;
import java.util.Set;
/**
* Implementation of the BFS algorithm.
*/
public class BreadthFirstSearch<V extends Vertex, E extends Edge<V>>
extends AbstractGraphPathSearch<V, E> {
@Override
public Result<V, E> search(Graph<V, E> graph, V src, V dst,
EdgeWeight<V, E> weight) {
checkArguments(graph, src, dst);
// Prepare the graph result.
DefaultResult result = new DefaultResult(src, dst);
// Setup the starting frontier with the source as the sole vertex.
Set<V> frontier = new HashSet<>();
result.updateVertex(src, null, 0.0, true);
frontier.add(src);
boolean reachedEnd = false;
while (!reachedEnd && !frontier.isEmpty()) {
// Prepare the next frontier.
Set<V> next = new HashSet<>();
// Visit all vertexes in the current frontier.
for (V vertex : frontier) {
double cost = result.cost(vertex);
// Visit all egress edges of the current frontier vertex.
for (E edge : graph.getEdgesFrom(vertex)) {
V nextVertex = edge.dst();
if (!result.hasCost(nextVertex)) {
// If this vertex has not been visited yet, update it.
double newCost = cost + (weight == null ? 1.0 : weight.weight(edge));
result.updateVertex(nextVertex, edge, newCost, true);
// If we have reached our intended destination, bail.
if (nextVertex.equals(dst)) {
reachedEnd = true;
break;
}
next.add(nextVertex);
}
if (reachedEnd) {
break;
}
}
}
// Promote the next frontier.
frontier = next;
}
// Finally, but the paths on the search result and return.
result.buildPaths();
return result;
}
}
package org.onlab.graph;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Simple concrete implementation of a directed graph path.
*/
public class DefaultMutablePath<V extends Vertex, E extends Edge<V>> implements MutablePath<V, E> {
private final List<E> edges = new ArrayList<>();
private double cost = 0.0;
/**
* Creates a new empty path.
*/
public DefaultMutablePath() {
}
/**
* Creates a new path as a copy of another path.
*
* @param path path to be copied
*/
public DefaultMutablePath(Path<V, E> path) {
checkNotNull(path, "Path cannot be null");
this.cost = path.cost();
edges.addAll(path.edges());
}
@Override
public V src() {
return edges.isEmpty() ? null : edges.get(0).src();
}
@Override
public V dst() {
return edges.isEmpty() ? null : edges.get(edges.size() - 1).dst();
}
@Override
public double cost() {
return cost;
}
@Override
public List<E> edges() {
return ImmutableList.copyOf(edges);
}
@Override
public void setCost(double cost) {
this.cost = cost;
}
@Override
public Path<V, E> toImmutable() {
return new DefaultPath<>(edges, cost);
}
@Override
public void insertEdge(E edge) {
checkNotNull(edge, "Edge cannot be null");
checkArgument(edges.isEmpty() || src().equals(edge.dst()),
"Edge destination must be the same as the current path source");
edges.add(0, edge);
}
@Override
public void appendEdge(E edge) {
checkNotNull(edge, "Edge cannot be null");
checkArgument(edges.isEmpty() || dst().equals(edge.src()),
"Edge source must be the same as the current path destination");
edges.add(edge);
}
@Override
public void removeEdge(E edge) {
checkArgument(edge.src().equals(edge.dst()) ||
edges.indexOf(edge) == 0 ||
edges.lastIndexOf(edge) == edges.size() - 1,
"Edge must be at start or end of path, or it must be a cyclic edge");
edges.remove(edge);
}
@Override
public String toString() {
return com.google.common.base.Objects.toStringHelper(this)
.add("src", src())
.add("dst", dst())
.add("cost", cost)
.add("edges", edges)
.toString();
}
@Override
public int hashCode() {
return Objects.hash(edges, cost);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DefaultMutablePath) {
final DefaultMutablePath other = (DefaultMutablePath) obj;
return Objects.equals(this.cost, other.cost) &&
Objects.equals(this.edges, other.edges);
}
return false;
}
}
package org.onlab.graph;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Simple concrete implementation of a directed graph path.
*/
public class DefaultPath<V extends Vertex, E extends Edge<V>> implements Path<V, E> {
private final V src;
private final V dst;
private final List<E> edges;
private double cost = 0.0;
/**
* Creates a new path from the specified list of edges and cost.
*
* @param edges list of path edges
* @param cost path cost as a unit-less number
*/
public DefaultPath(List<E> edges, double cost) {
checkNotNull(edges, "Edges list must not be null");
checkArgument(!edges.isEmpty(), "There must be at least one edge");
this.edges = ImmutableList.copyOf(edges);
this.src = edges.get(0).src();
this.dst = edges.get(edges.size() - 1).dst();
this.cost = cost;
}
@Override
public V src() {
return src;
}
@Override
public V dst() {
return dst;
}
@Override
public double cost() {
return cost;
}
@Override
public List<E> edges() {
return Collections.unmodifiableList(edges);
}
@Override
public String toString() {
return com.google.common.base.Objects.toStringHelper(this)
.add("src", src)
.add("dst", dst)
.add("cost", cost)
.add("edges", edges)
.toString();
}
@Override
public int hashCode() {
return Objects.hash(src, dst, edges, cost);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof DefaultPath) {
final DefaultPath other = (DefaultPath) obj;
return Objects.equals(this.src, other.src) &&
Objects.equals(this.dst, other.dst) &&
Objects.equals(this.cost, other.cost) &&
Objects.equals(this.edges, other.edges);
}
return false;
}
}
package org.onlab.graph;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
* DFS graph search algorithm implemented via iteration rather than recursion.
*/
public class DepthFirstSearch<V extends Vertex, E extends Edge<V>>
extends AbstractGraphPathSearch<V, E> {
/**
* Graph edge types as classified by the DFS algorithm.
*/
public static enum EdgeType {
TREE_EDGE, FORWARD_EDGE, BACK_EDGE, CROSS_EDGE
}
@Override
public SpanningTreeResult search(Graph<V, E> graph, V src, V dst,
EdgeWeight<V, E> weight) {
checkArguments(graph, src, dst);
// Prepare the search result.
SpanningTreeResult result = new SpanningTreeResult(src, dst);
// The source vertex has cost 0, of course.
result.updateVertex(src, null, 0.0, true);
// Track finished vertexes and keep a stack of vertexes that have been
// started; start this stack with the source on it.
Set<V> finished = new HashSet<>();
Stack<V> stack = new Stack<>();
stack.push(src);
while (!stack.isEmpty()) {
V vertex = stack.peek();
if (vertex.equals(dst)) {
// If we have reached our destination, bail.
break;
}
double cost = result.cost(vertex);
boolean tangent = false;
// Visit all egress edges of the current vertex.
for (E edge : graph.getEdgesFrom(vertex)) {
// If we have seen the edge already, skip it.
if (result.isEdgeMarked(edge)) {
continue;
}
// Examine the destination of the current edge.
V nextVertex = edge.dst();
if (!result.hasCost(nextVertex)) {
// If this vertex have not finished this vertex yet,
// not started it, then start it as a tree-edge.
result.markEdge(edge, EdgeType.TREE_EDGE);
double newCost = cost + (weight == null ? 1.0 : weight.weight(edge));
result.updateVertex(nextVertex, edge, newCost, true);
stack.push(nextVertex);
tangent = true;
break;
} else if (!finished.contains(nextVertex)) {
// We started the vertex, but did not yet finish it, so
// it must be a back-edge.
result.markEdge(edge, EdgeType.BACK_EDGE);
} else {
// The target has been finished already, so what we have
// here is either a forward-edge or a cross-edge.
result.markEdge(edge, isForwardEdge(result, edge) ?
EdgeType.FORWARD_EDGE : EdgeType.CROSS_EDGE);
}
}
// If we have not been sent on a tangent search and reached the
// end of the current scan normally, mark the node as finished
// and pop it off the vertex stack.
if (!tangent) {
finished.add(vertex);
stack.pop();
}
}
// Finally, but the paths on the search result and return.
result.buildPaths();
return result;
}
/**
* Determines whether the specified edge is a forward edge using the
* accumulated set of parent edges for each vertex.
*
* @param result search result
* @param edge edge to be classified
* @return true if the edge is a forward edge
*/
protected boolean isForwardEdge(DefaultResult result, E edge) {
// Follow the parent edges until we hit the edge source vertex
V target = edge.src();
V vertex = edge.dst();
Set<E> parentEdges;
while ((parentEdges = result.parents.get(vertex)) != null) {
for (E parentEdge : parentEdges) {
vertex = parentEdge.src();
if (vertex.equals(target)) {
return true;
}
}
}
return false;
}
/**
* Graph search result which includes edge classification for building
* a spanning tree.
*/
public class SpanningTreeResult extends DefaultResult {
protected final Map<E, EdgeType> edges = new HashMap<>();
/**
* Creates a new spanning tree result.
*
* @param src search source
* @param dst optional search destination
*/
public SpanningTreeResult(V src, V dst) {
super(src, dst);
}
/**
* Returns the map of edge type.
*
* @return edge to edge type bindings
*/
public Map<E, EdgeType> edges() {
return edges;
}
/**
* Indicates whether or not the edge has been marked with type.
*
* @param edge edge to test
* @return true if the edge has been marked already
*/
boolean isEdgeMarked(E edge) {
return edges.containsKey(edge);
}
/**
* Marks the edge with the specified type.
*
* @param edge edge to mark
* @param type edge type
*/
void markEdge(E edge, EdgeType type) {
edges.put(edge, type);
}
}
}
package org.onlab.graph;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Set;
/**
* Dijkstra shortest-path graph search algorithm capable of finding not just
* one, but all shortest paths between the source and destinations.
*/
public class DijkstraGraphSearch<V extends Vertex, E extends Edge<V>>
extends AbstractGraphPathSearch<V, E> {
@Override
public Result<V, E> search(Graph<V, E> graph, V src, V dst,
EdgeWeight<V, E> weight) {
checkArguments(graph, src, dst);
// Use the default result to remember cumulative costs and parent
// edges to each each respective vertex.
DefaultResult result = new DefaultResult(src, dst);
// Cost to reach the source vertex is 0 of course.
result.updateVertex(src, null, 0.0, false);
// Use the min priority queue to progressively find each nearest
// vertex until we reach the desired destination, if one was given,
// or until we reach all possible destinations.
Heap<V> minQueue = createMinQueue(graph.getVertexes(),
new PathCostComparator(result));
while (!minQueue.isEmpty()) {
// Get the nearest vertex
V nearest = minQueue.extractExtreme();
if (nearest.equals(dst)) {
break;
}
// Find its cost and use it to determine if the vertex is reachable.
double cost = result.cost(nearest);
if (cost < Double.MAX_VALUE) {
// If the vertex is reachable, relax all its egress edges.
for (E e : graph.getEdgesFrom(nearest)) {
result.relaxEdge(e, cost, weight);
}
}
// Re-prioritize the min queue.
minQueue.heapify();
}
// Now construct a set of paths from the results.
result.buildPaths();
return result;
}
// Compares path weights using their accrued costs; used for sorting the
// min priority queue.
private final class PathCostComparator implements Comparator<V> {
private final DefaultResult result;
private PathCostComparator(DefaultResult result) {
this.result = result;
}
@Override
public int compare(V v1, V v2) {
double delta = result.cost(v2) - result.cost(v1);
return delta < 0 ? -1 : (delta > 0 ? 1 : 0);
}
}
// Creates a min priority queue from the specified vertexes and comparator.
private Heap<V> createMinQueue(Set<V> vertexes, Comparator<V> comparator) {
return new Heap<>(new ArrayList<>(vertexes), comparator);
}
}
package org.onlab.graph;
/**
* Representation of a graph edge.
*
* @param <V> vertex type
*/
public interface Edge<V extends Vertex> {
/**
* Returns the edge source vertex.
*
* @return source vertex
*/
V src();
/**
* Returns the edge destination vertex.
*
* @return destination vertex
*/
V dst();
}
package org.onlab.graph;
/**
* Abstraction of a graph edge weight function.
*/
public interface EdgeWeight<V extends Vertex, E extends Edge<V>> {
/**
* Returns the weight of the given edge as a unit-less number.
*
* @param edge edge to be weighed
* @return edge weight as a unit-less number
*/
double weight(E edge);
}
package org.onlab.graph;
import java.util.Set;
/**
* Abstraction of a directed graph structure.
*
* @param <V> vertex type
* @param <E> edge type
*/
public interface Graph<V extends Vertex, E extends Edge> {
/**
* Returns the set of vertexes comprising the graph.
*
* @return set of vertexes
*/
Set<V> getVertexes();
/**
* Returns the set of edges comprising the graph.
*
* @return set of edges
*/
Set<E> getEdges();
/**
* Returns all edges leading out from the specified source vertex.
*
* @param src source vertex
* @return set of egress edges; empty if no such edges
*/
Set<E> getEdgesFrom(V src);
/**
* Returns all edges leading towards the specified destination vertex.
*
* @param dst destination vertex
* @return set of ingress vertexes; empty if no such edges
*/
Set<E> getEdgesTo(V dst);
}
package org.onlab.graph;
import java.util.Map;
import java.util.Set;
/**
* Representation of a graph path search algorithm.
*
* @param <V> vertex type
* @param <E> edge type
*/
public interface GraphPathSearch<V extends Vertex, E extends Edge<V>> {
/**
* Abstraction of a path search result.
*/
public interface Result<V extends Vertex, E extends Edge<V>> {
/**
* Returns the search source.
*
* @return search source
*/
public V src();
/**
* Returns the search destination, if was was given.
*
* @return optional search destination
*/
public V dst();
/**
* Returns the set of paths produced as a result of the graph search.
*
* @return set of paths
*/
Set<Path<V, E>> paths();
/**
* Returns bindings of each vertex to its parent edges in the path.
*
* @return map of vertex to its parent edge bindings
*/
public Map<V, Set<E>> parents();
/**
* Return a bindings of each vertex to its cost in the path.
*
* @return map of vertex to path cost bindings
*/
public Map<V, Double> costs();
}
/**
* Searches the specified graph.
*
* @param graph graph to be searched
* @param src optional source vertex
* @param dst optional destination vertex; if null paths to all vertex
* destinations will be searched
* @param weight optional edge-weight; if null cost of each edge will be
* assumed to be 1.0
* @return search results
*/
Result<V, E> search(Graph<V, E> graph, V src, V dst, EdgeWeight<V, E> weight);
}
package org.onlab.graph;
/**
* Representation of a graph search algorithm and its outcome.
*
* @param <V> vertex type
* @param <E> edge type
*/
public interface GraphSearch<V extends Vertex, E extends Edge<V>> {
/**
* Notion of a graph search result.
*/
public interface Result<V extends Vertex, E extends Edge<V>> {
}
/**
* Searches the specified graph.
*
* @param graph graph to be searched
* @param weight optional edge-weight; if null cost of each edge will be
* assumed to be 1.0
*
* @return search results
*/
Result search(Graph<V, E> graph, EdgeWeight<V, E> weight);
}
package org.onlab.graph;
import com.google.common.collect.ImmutableList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Implementation of an array-backed heap structure whose sense of order is
* imposed by the provided comparator.
* <p/>
* While this provides similar functionality to {@link java.util.PriorityQueue}
* data structure, one key difference is that external entities can control
* when to restore the heap property, which is done through invocation of the
* {@link #heapify} method.
* <p/>
* This class is not thread-safe and care must be taken to prevent concurrent
* modifications.
*
* @param <T> type of the items on the heap
*/
public class Heap<T> {
private final List<T> data;
private final Comparator<T> comparator;
/**
* Creates a new heap backed by the specified list. In the interest of
* efficiency, the list should be array-backed. Also, for the same reason,
* the data is not copied and therefore, the caller must assure that the
* backing data is not altered in any way.
*
* @param data backing data list
* @param comparator comparator for ordering the heap items
*/
public Heap(List<T> data, Comparator<T> comparator) {
this.data = checkNotNull(data, "Data cannot be null");
this.comparator = checkNotNull(comparator, "Comparator cannot be null");
heapify();
}
/**
* Restores the heap property by re-arranging the elements in the backing
* array as necessary following any heap modifications.
*/
public void heapify() {
for (int i = data.size() / 2; i >= 0; i--) {
heapify(i);
}
}
/**
* Returns the current size of the heap.
*
* @return number of items in the heap
*/
public int size() {
return data.size();
}
/**
* Returns true if there are no items in the heap.
*
* @return true if heap is empty
*/
public boolean isEmpty() {
return data.isEmpty();
}
/**
* Returns the most extreme item in the heap.
*
* @return heap extreme or null if the heap is empty
*/
public T extreme() {
return data.isEmpty() ? null : data.get(0);
}
/**
* Extracts and returns the most extreme item from the heap.
*
* @return heap extreme or null if the heap is empty
*/
public T extractExtreme() {
if (!isEmpty()) {
T extreme = extreme();
data.set(0, data.get(data.size() - 1));
data.remove(data.size() - 1);
heapify();
return extreme;
}
return null;
}
/**
* Inserts the specified item into the heap and returns the modified heap.
*
* @param item item to be inserted
* @return the heap self
* @throws IllegalArgumentException if the heap is already full
*/
public Heap<T> insert(T item) {
data.add(item);
bubbleUp();
return this;
}
/**
* Returns iterator to traverse the heap level-by-level. This iterator
* does not permit removal of items.
*
* @return non-destructive heap iterator
*/
public Iterator<T> iterator() {
return ImmutableList.copyOf(data).iterator();
}
// Bubbles up the last item in the heap to its proper position to restore
// the heap property.
private void bubbleUp() {
int child = data.size() - 1;
while (child > 0) {
int parent = child / 2;
if (comparator.compare(data.get(child), data.get(parent)) < 0) {
break;
}
swap(child, parent);
child = parent;
}
}
// Restores the heap property of the specified heap layer.
private void heapify(int i) {
int left = 2 * i + 1;
int right = 2 * i;
int extreme = i;
if (left < data.size() &&
comparator.compare(data.get(extreme), data.get(left)) < 0) {
extreme = left;
}
if (right < data.size() &&
comparator.compare(data.get(extreme), data.get(right)) < 0) {
extreme = right;
}
if (extreme != i) {
swap(i, extreme);
heapify(extreme);
}
}
// Swaps two heap items identified by their respective indexes.
private void swap(int i, int k) {
T aux = data.get(i);
data.set(i, data.get(k));
data.set(k, aux);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Heap) {
Heap that = (Heap) obj;
return this.getClass() == that.getClass() &&
Objects.equals(this.comparator, that.comparator) &&
Objects.deepEquals(this.data, that.data);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(comparator, data);
}
@Override
public String toString() {
return com.google.common.base.Objects.toStringHelper(this)
.add("data", data)
.add("comparator", comparator)
.toString();
}
}
package org.onlab.graph;
/**
* Abstraction of a mutable graph that can be constructed gradually.
*/
public interface MutableGraph<V extends Vertex, E extends Edge> extends Graph<V, E> {
/**
* Adds the specified vertex to this graph.
*
* @param vertex new vertex
*/
void addVertex(V vertex);
/**
* Removes the specified vertex from the graph.
*
* @param vertex vertex to be removed
*/
void removeVertex(V vertex);
/**
* Adds the specified edge to this graph. If the edge vertexes are not
* already in the graph, they will be added as well.
*
* @param edge new edge
*/
void addEdge(E edge);
/**
* Removes the specified edge from the graph.
*
* @param edge edge to be removed
*/
void removeEdge(E edge);
/**
* Returns an immutable copy of this graph.
*
* @return immutable copy
*/
Graph<V, E> toImmutable();
}
package org.onlab.graph;
/**
* Abstraction of a mutable path that allows gradual construction.
*/
public interface MutablePath<V extends Vertex, E extends Edge<V>> extends Path<V, E> {
/**
* Inserts a new edge at the beginning of this path. The edge must be
* adjacent to the prior start of the path.
*
* @param edge edge to be inserted
*/
void insertEdge(E edge);
/**
* Appends a new edge at the end of the this path. The edge must be
* adjacent to the prior end of the path.
*
* @param edge edge to be inserted
*/
void appendEdge(E edge);
/**
* Removes the specified edge. This edge must be either at the start or
* at the end of the path, or it must be a cyclic edge in order not to
* violate the contiguous path property.
*
* @param edge edge to be removed
*/
void removeEdge(E edge);
/**
* Sets the total path cost as a unit-less double.
*
* @param cost new path cost
*/
void setCost(double cost);
/**
* Returns an immutable copy of this path.
*
* @return immutable copy
*/
Path<V, E> toImmutable();
}
package org.onlab.graph;
import java.util.List;
/**
* Representation of a path in a graph as a sequence of edges. Paths are
* assumed to be continuous, where adjacent edges must share a vertex.
*
* @param <V> vertex type
* @param <E> edge type
*/
public interface Path<V extends Vertex, E extends Edge<V>> extends Edge<V> {
/**
* Returns the list of edges comprising the path. Adjacent edges will
* share the same vertex, meaning that a source of one edge, will be the
* same as the destination of the prior edge.
*
* @return list of path edges
*/
List<E> edges();
/**
* Returns the total cost of the path as a unit-less number.
*
* @return path cost as a unit-less number
*/
double cost();
}
package org.onlab.graph;
/**
* Representation of a graph vertex.
*/
public interface Vertex {
}
<body>
Graph abstractions and graph path finding algorithms.
</body>
\ No newline at end of file
<body>
Miscellaneous domain-agnostic utilities.
</body>
\ No newline at end of file
package org.onlab.graph;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
/**
* Test of the base edge implementation.
*/
public class AbstractEdgeTest {
@Test
public void equality() {
TestVertex v1 = new TestVertex("1");
TestVertex v2 = new TestVertex("2");
new EqualsTester()
.addEqualityGroup(new TestEdge(v1, v2, 1),
new TestEdge(v1, v2, 1))
.addEqualityGroup(new TestEdge(v2, v1, 1))
.testEquals();
}
}
package org.onlab.graph;
import org.junit.Test;
import static com.google.common.collect.ImmutableSet.of;
import static org.junit.Assert.assertEquals;
/**
* Base for all graph search tests.
*/
public abstract class AbstractGraphPathSearchTest extends GraphTest {
/**
* Creates a test-specific graph search to exercise.
*
* @return graph search
*/
protected abstract AbstractGraphPathSearch<TestVertex, TestEdge> graphSearch();
@Test(expected = IllegalArgumentException.class)
public void noSuchSourceArgument() {
graphSearch().search(new AdjacencyListsGraph<>(of(B, C),
of(new TestEdge(B, C, 1))),
A, H, weight);
}
@Test(expected = NullPointerException.class)
public void nullGraphArgument() {
graphSearch().search(null, A, H, weight);
}
@Test(expected = NullPointerException.class)
public void nullSourceArgument() {
graphSearch().search(new AdjacencyListsGraph<>(of(B, C),
of(new TestEdge(B, C, 1))),
null, H, weight);
}
@Test
public void samenessThreshold() {
AbstractGraphPathSearch<TestVertex, TestEdge> search = graphSearch();
search.setSamenessThreshold(0.3);
assertEquals("incorrect threshold", 0.3, search.samenessThreshold(), 0.01);
}
}
package org.onlab.graph;
import com.google.common.collect.ImmutableSet;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import java.util.Set;
import static org.junit.Assert.assertEquals;
/**
* Tests of the graph implementation.
*/
public class AdjacencyListsGraphTest {
private static final TestVertex A = new TestVertex("A");
private static final TestVertex B = new TestVertex("B");
private static final TestVertex C = new TestVertex("C");
private static final TestVertex D = new TestVertex("D");
private static final TestVertex E = new TestVertex("E");
private static final TestVertex F = new TestVertex("F");
private static final TestVertex G = new TestVertex("G");
private final Set<TestEdge> edges =
ImmutableSet.of(new TestEdge(A, B, 1), new TestEdge(B, C, 1),
new TestEdge(C, D, 1), new TestEdge(D, A, 1),
new TestEdge(B, D, 1));
@Test
public void equality() {
Set<TestVertex> vertexes = ImmutableSet.of(A, B, C, D, E, F);
Set<TestVertex> vertexes2 = ImmutableSet.of(A, B, C, D, E, F, G);
AdjacencyListsGraph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(vertexes, edges);
AdjacencyListsGraph<TestVertex, TestEdge> same = new AdjacencyListsGraph<>(vertexes, edges);
AdjacencyListsGraph<TestVertex, TestEdge> different = new AdjacencyListsGraph<>(vertexes2, edges);
new EqualsTester()
.addEqualityGroup(graph, same)
.addEqualityGroup(different)
.testEquals();
}
@Test
public void basics() {
Set<TestVertex> vertexes = ImmutableSet.of(A, B, C, D, E, F);
AdjacencyListsGraph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(vertexes, edges);
assertEquals("incorrect vertex count", 6, graph.getVertexes().size());
assertEquals("incorrect edge count", 5, graph.getEdges().size());
assertEquals("incorrect egress edge count", 1, graph.getEdgesFrom(A).size());
assertEquals("incorrect ingress edge count", 1, graph.getEdgesTo(A).size());
assertEquals("incorrect ingress edge count", 1, graph.getEdgesTo(C).size());
assertEquals("incorrect egress edge count", 2, graph.getEdgesFrom(B).size());
assertEquals("incorrect ingress edge count", 2, graph.getEdgesTo(D).size());
}
}
package org.onlab.graph;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertEquals;
/**
* Test of the Bellman-Ford algorithm.
*/
public class BellmanFordGraphSearchTest extends BreadthFirstSearchTest {
@Override
protected AbstractGraphPathSearch<TestVertex, TestEdge> graphSearch() {
return new BellmanFordGraphSearch<>();
}
@Test
@Override
public void defaultGraphTest() {
executeDefaultTest(7, 5, 5.0);
}
@Test
public void defaultHopCountWeight() {
weight = null;
executeDefaultTest(10, 3, 3.0);
}
@Test
public void searchGraphWithNegativeCycles() {
Set<TestVertex> vertexes = new HashSet<>(vertices());
vertexes.add(Z);
Set<TestEdge> edges = new HashSet<>(edges());
edges.add(new TestEdge(G, Z, 1.0));
edges.add(new TestEdge(Z, G, -2.0));
g = new AdjacencyListsGraph<>(vertexes, edges);
GraphPathSearch<TestVertex, TestEdge> search = graphSearch();
Set<Path<TestVertex, TestEdge>> paths = search.search(g, A, H, weight).paths();
assertEquals("incorrect paths count", 1, paths.size());
Path p = paths.iterator().next();
assertEquals("incorrect src", A, p.src());
assertEquals("incorrect dst", H, p.dst());
assertEquals("incorrect path length", 5, p.edges().size());
assertEquals("incorrect path cost", 5.0, p.cost(), 0.1);
paths = search.search(g, A, G, weight).paths();
assertEquals("incorrect paths count", 0, paths.size());
paths = search.search(g, A, null, weight).paths();
printPaths(paths);
assertEquals("incorrect paths count", 6, paths.size());
}
}
package org.onlab.graph;
import org.junit.Test;
import java.util.Set;
import static org.junit.Assert.assertEquals;
/**
* Test of the BFS and similar path search algorithms.
*/
public class BreadthFirstSearchTest extends AbstractGraphPathSearchTest {
@Override
protected AbstractGraphPathSearch<TestVertex, TestEdge> graphSearch() {
return new BreadthFirstSearch<>();
}
@Test
public void defaultGraphTest() {
executeDefaultTest(7, 3, 8.0);
}
@Test
public void defaultHopCountWeight() {
weight = null;
executeDefaultTest(7, 3, 3.0);
}
// Executes the default test
protected void executeDefaultTest(int pathCount, int pathLength, double pathCost) {
g = new AdjacencyListsGraph<>(vertices(), edges());
GraphPathSearch<TestVertex, TestEdge> search = graphSearch();
Set<Path<TestVertex, TestEdge>> paths = search.search(g, A, H, weight).paths();
assertEquals("incorrect paths count", 1, paths.size());
Path p = paths.iterator().next();
assertEquals("incorrect src", A, p.src());
assertEquals("incorrect dst", H, p.dst());
assertEquals("incorrect path length", pathLength, p.edges().size());
assertEquals("incorrect path cost", pathCost, p.cost(), 0.1);
paths = search.search(g, A, null, weight).paths();
printPaths(paths);
assertEquals("incorrect paths count", pathCount, paths.size());
}
// Executes the search and validates its results.
protected void executeSearch(GraphPathSearch<TestVertex, TestEdge> search,
Graph<TestVertex, TestEdge> graph,
TestVertex src, TestVertex dst,
EdgeWeight<TestVertex, TestEdge> weight,
int pathCount, double pathCost) {
GraphPathSearch.Result<TestVertex, TestEdge> result =
search.search(graph, src, dst, weight);
Set<Path<TestVertex, TestEdge>> paths = result.paths();
printPaths(paths);
assertEquals("incorrect paths count", pathCount, paths.size());
if (pathCount > 0) {
Path<TestVertex, TestEdge> path = paths.iterator().next();
assertEquals("incorrect path cost", pathCost, path.cost(), 0.1);
}
}
}
package org.onlab.graph;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import static com.google.common.collect.ImmutableList.of;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
* Test of the default mutable path.
*/
public class DefaultMutablePathTest extends DefaultPathTest {
@Test
public void equality() {
DefaultPath<TestVertex, TestEdge> p1 =
new DefaultPath<>(of(new TestEdge(A, B, 1),
new TestEdge(B, C, 1)), 2.0);
DefaultPath<TestVertex, TestEdge> p2 =
new DefaultPath<>(of(new TestEdge(A, B, 1),
new TestEdge(B, D, 1)), 2.0);
new EqualsTester().addEqualityGroup(new DefaultMutablePath<>(p1),
new DefaultMutablePath<>(p1))
.addEqualityGroup(new DefaultMutablePath<>(p2))
.testEquals();
}
@Test
public void empty() {
MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>();
assertNull("src should be null", p.src());
assertNull("dst should be null", p.dst());
assertEquals("incorrect edge count", 0, p.edges().size());
assertEquals("incorrect path cost", 0.0, p.cost(), 0.1);
}
@Test
public void pathCost() {
MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>();
p.setCost(4);
assertEquals("incorrect path cost", 4.0, p.cost(), 0.1);
}
private void validatePath(Path<TestVertex, TestEdge> p,
TestVertex src, TestVertex dst, int length) {
validatePath(p, src, dst, length, 0.0);
}
@Test
public void insertEdge() {
MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>();
p.insertEdge(new TestEdge(B, C, 1));
p.insertEdge(new TestEdge(A, B, 1));
validatePath(p, A, C, 2);
}
@Test
public void appendEdge() {
MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>();
p.appendEdge(new TestEdge(A, B, 1));
p.appendEdge(new TestEdge(B, C, 1));
validatePath(p, A, C, 2);
}
@Test
public void removeEdge() {
MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>();
p.appendEdge(new TestEdge(A, B, 1));
p.appendEdge(new TestEdge(B, C, 1));
p.appendEdge(new TestEdge(C, C, 2));
p.appendEdge(new TestEdge(C, D, 1));
validatePath(p, A, D, 4);
p.removeEdge(new TestEdge(A, B, 1));
validatePath(p, B, D, 3);
p.removeEdge(new TestEdge(C, C, 2));
validatePath(p, B, D, 2);
p.removeEdge(new TestEdge(C, D, 1));
validatePath(p, B, C, 1);
}
@Test
public void toImmutable() {
MutablePath<TestVertex, TestEdge> p = new DefaultMutablePath<>();
p.appendEdge(new TestEdge(A, B, 1));
p.appendEdge(new TestEdge(B, C, 1));
validatePath(p, A, C, 2);
assertEquals("immutables should equal", p.toImmutable(), p.toImmutable());
validatePath(p.toImmutable(), A, C, 2);
}
}
package org.onlab.graph;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import java.util.List;
import static com.google.common.collect.ImmutableList.of;
import static org.junit.Assert.assertEquals;
/**
* Test of the default path.
*/
public class DefaultPathTest extends GraphTest {
@Test
public void equality() {
List<TestEdge> edges = of(new TestEdge(A, B, 1), new TestEdge(B, C, 1));
new EqualsTester().addEqualityGroup(new DefaultPath<>(edges, 2.0),
new DefaultPath<>(edges, 2.0))
.addEqualityGroup(new DefaultPath<>(edges, 3.0))
.testEquals();
}
@Test
public void basics() {
Path<TestVertex, TestEdge> p = new DefaultPath<>(of(new TestEdge(A, B, 1),
new TestEdge(B, C, 1)), 2.0);
validatePath(p, A, C, 2, 2.0);
}
// Validates the path against expected attributes
protected void validatePath(Path<TestVertex, TestEdge> p,
TestVertex src, TestVertex dst,
int length, double cost) {
assertEquals("incorrect path length", length, p.edges().size());
assertEquals("incorrect source", src, p.src());
assertEquals("incorrect destination", dst, p.dst());
assertEquals("incorrect path cost", cost, p.cost(), 0.1);
}
}
package org.onlab.graph;
import org.junit.Test;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.onlab.graph.DepthFirstSearch.EdgeType;
/**
* Test of the DFS algorithm.
*/
public class DepthFirstSearchTest extends AbstractGraphPathSearchTest {
@Override
protected DepthFirstSearch<TestVertex, TestEdge> graphSearch() {
return new DepthFirstSearch<>();
}
@Test
public void defaultGraphTest() {
executeDefaultTest(3, 6, 5.0, 12.0);
executeBroadSearch();
}
@Test
public void defaultHopCountWeight() {
weight = null;
executeDefaultTest(3, 6, 3.0, 6.0);
executeBroadSearch();
}
protected void executeDefaultTest(int minLength, int maxLength,
double minCost, double maxCost) {
g = new AdjacencyListsGraph<>(vertices(), edges());
DepthFirstSearch<TestVertex, TestEdge> search = graphSearch();
DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result =
search.search(g, A, H, weight);
Set<Path<TestVertex, TestEdge>> paths = result.paths();
assertEquals("incorrect path count", 1, paths.size());
Path path = paths.iterator().next();
System.out.println(path);
assertEquals("incorrect src", A, path.src());
assertEquals("incorrect dst", H, path.dst());
int l = path.edges().size();
assertTrue("incorrect path length " + l,
minLength <= l && l <= maxLength);
assertTrue("incorrect path cost " + path.cost(),
minCost <= path.cost() && path.cost() <= maxCost);
System.out.println(result.edges());
printPaths(paths);
}
public void executeBroadSearch() {
g = new AdjacencyListsGraph<>(vertices(), edges());
DepthFirstSearch<TestVertex, TestEdge> search = graphSearch();
// Perform narrow path search to a specific destination.
DepthFirstSearch<TestVertex, TestEdge>.SpanningTreeResult result =
search.search(g, A, null, weight);
assertEquals("incorrect paths count", 7, result.paths().size());
int[] types = new int[]{0, 0, 0, 0};
for (EdgeType t : result.edges().values()) {
types[t.ordinal()] += 1;
}
assertEquals("incorrect tree-edge count", 7,
types[EdgeType.TREE_EDGE.ordinal()]);
assertEquals("incorrect back-edge count", 1,
types[EdgeType.BACK_EDGE.ordinal()]);
assertEquals("incorrect cross-edge & forward-edge count", 4,
types[EdgeType.FORWARD_EDGE.ordinal()] +
types[EdgeType.CROSS_EDGE.ordinal()]);
}
}
package org.onlab.graph;
import org.junit.Test;
import java.util.Set;
import static com.google.common.collect.ImmutableSet.of;
import static org.junit.Assert.assertEquals;
/**
* Test of the Dijkstra algorithm.
*/
public class DijkstraGraphSearchTest extends BreadthFirstSearchTest {
@Override
protected AbstractGraphPathSearch<TestVertex, TestEdge> graphSearch() {
return new DijkstraGraphSearch<>();
}
@Test
@Override
public void defaultGraphTest() {
executeDefaultTest(7, 5, 5.0);
}
@Test
@Override
public void defaultHopCountWeight() {
weight = null;
executeDefaultTest(10, 3, 3.0);
}
@Test
public void noPath() {
g = new AdjacencyListsGraph<>(of(A, B, C, D),
of(new TestEdge(A, B, 1),
new TestEdge(B, A, 1),
new TestEdge(C, D, 1),
new TestEdge(D, C, 1)));
GraphPathSearch<TestVertex, TestEdge> gs = graphSearch();
Set<Path<TestVertex, TestEdge>> paths = gs.search(g, A, B, weight).paths();
printPaths(paths);
assertEquals("incorrect paths count", 1, paths.size());
assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1);
paths = gs.search(g, A, D, weight).paths();
printPaths(paths);
assertEquals("incorrect paths count", 0, paths.size());
paths = gs.search(g, A, null, weight).paths();
printPaths(paths);
assertEquals("incorrect paths count", 1, paths.size());
assertEquals("incorrect path cost", 1.0, paths.iterator().next().cost(), 0.1);
}
@Test
public void simpleMultiplePath() {
g = new AdjacencyListsGraph<>(of(A, B, C, D),
of(new TestEdge(A, B, 1),
new TestEdge(A, C, 1),
new TestEdge(B, D, 1),
new TestEdge(C, D, 1)));
executeSearch(graphSearch(), g, A, D, weight, 2, 2.0);
}
@Test
public void denseMultiplePath() {
g = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G),
of(new TestEdge(A, B, 1),
new TestEdge(A, C, 1),
new TestEdge(B, D, 1),
new TestEdge(C, D, 1),
new TestEdge(D, E, 1),
new TestEdge(D, F, 1),
new TestEdge(E, G, 1),
new TestEdge(F, G, 1),
new TestEdge(A, G, 4)));
executeSearch(graphSearch(), g, A, G, weight, 5, 4.0);
}
@Test
public void dualEdgeMultiplePath() {
g = new AdjacencyListsGraph<>(of(A, B, C, D, E, F, G, H),
of(new TestEdge(A, B, 1), new TestEdge(A, C, 3),
new TestEdge(B, D, 2), new TestEdge(B, C, 1),
new TestEdge(B, E, 4), new TestEdge(C, E, 1),
new TestEdge(D, H, 5), new TestEdge(D, E, 1),
new TestEdge(E, F, 1), new TestEdge(F, D, 1),
new TestEdge(F, G, 1), new TestEdge(F, H, 1),
new TestEdge(A, E, 3), new TestEdge(B, D, 1)));
executeSearch(graphSearch(), g, A, E, weight, 3, 3.0);
}
}
package org.onlab.graph;
import java.util.Set;
import static com.google.common.collect.ImmutableSet.of;
/**
* Base class for various graph-related tests.
*/
public class GraphTest {
static final TestVertex A = new TestVertex("A");
static final TestVertex B = new TestVertex("B");
static final TestVertex C = new TestVertex("C");
static final TestVertex D = new TestVertex("D");
static final TestVertex E = new TestVertex("E");
static final TestVertex F = new TestVertex("F");
static final TestVertex G = new TestVertex("G");
static final TestVertex H = new TestVertex("H");
static final TestVertex Z = new TestVertex("Z");
protected Graph<TestVertex, TestEdge> g;
protected EdgeWeight<TestVertex, TestEdge> weight =
new EdgeWeight<TestVertex, TestEdge>() {
@Override
public double weight(TestEdge edge) {
return edge.weight();
}
};
protected void printPaths(Set<Path<TestVertex, TestEdge>> paths) {
for (Path p : paths) {
System.out.println(p);
}
}
protected Set<TestVertex> vertices() {
return of(A, B, C, D, E, F, G, H);
}
protected Set<TestEdge> edges() {
return of(new TestEdge(A, B, 1), new TestEdge(A, C, 3),
new TestEdge(B, D, 2), new TestEdge(B, C, 1),
new TestEdge(B, E, 4), new TestEdge(C, E, 1),
new TestEdge(D, H, 5), new TestEdge(D, E, 1),
new TestEdge(E, F, 1), new TestEdge(F, D, 1),
new TestEdge(F, G, 1), new TestEdge(F, H, 1));
}
}
package org.onlab.graph;
import com.google.common.collect.Ordering;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Comparator;
import static com.google.common.collect.ImmutableList.of;
import static org.junit.Assert.*;
/**
* Heap data structure tests.
*/
public class HeapTest {
private ArrayList<Integer> data =
new ArrayList<>(of(6, 4, 5, 9, 8, 3, 2, 1, 7, 0));
private static final Comparator<Integer> MIN = Ordering.natural().reverse();
private static final Comparator<Integer> MAX = Ordering.natural();
@Test
public void equality() {
new EqualsTester()
.addEqualityGroup(new Heap<>(data, MIN),
new Heap<>(data, MIN))
.addEqualityGroup(new Heap<>(data, MAX))
.testEquals();
}
@Test
public void empty() {
Heap<Integer> h = new Heap<>(new ArrayList<Integer>(), MIN);
assertTrue("should be empty", h.isEmpty());
assertEquals("incorrect size", 0, h.size());
assertNull("no item expected", h.extreme());
assertNull("no item expected", h.extractExtreme());
}
@Test
public void insert() {
Heap<Integer> h = new Heap<>(data, MIN);
assertEquals("incorrect size", 10, h.size());
h.insert(3);
assertEquals("incorrect size", 11, h.size());
}
@Test
public void minQueue() {
Heap<Integer> h = new Heap<>(data, MIN);
assertFalse("should not be empty", h.isEmpty());
assertEquals("incorrect size", 10, h.size());
assertEquals("incorrect extreme", (Integer) 0, h.extreme());
for (int i = 0, n = h.size(); i < n; i++) {
assertEquals("incorrect element", (Integer) i, h.extractExtreme());
}
assertTrue("should be empty", h.isEmpty());
}
@Test
public void maxQueue() {
Heap<Integer> h = new Heap<>(data, MAX);
assertFalse("should not be empty", h.isEmpty());
assertEquals("incorrect size", 10, h.size());
assertEquals("incorrect extreme", (Integer) 9, h.extreme());
for (int i = h.size(); i > 0; i--) {
assertEquals("incorrect element", (Integer) (i - 1), h.extractExtreme());
}
assertTrue("should be empty", h.isEmpty());
}
@Test
public void iterator() {
Heap<Integer> h = new Heap<>(data, MIN);
assertTrue("should have next element", h.iterator().hasNext());
}
}
package org.onlab.graph;
import java.util.Objects;
import static com.google.common.base.Objects.toStringHelper;
/**
* Test edge.
*/
public class TestEdge extends AbstractEdge<TestVertex> {
private final double weight;
/**
* Creates a new edge between the specified source and destination vertexes.
*
* @param src source vertex
* @param dst destination vertex
* @param weight edge weight
*/
public TestEdge(TestVertex src, TestVertex dst, double weight) {
super(src, dst);
this.weight = weight;
}
/**
* Returns the edge weight.
*
* @return edge weight
*/
public double weight() {
return weight;
}
@Override
public int hashCode() {
return 31 * super.hashCode() + Objects.hash(weight);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof TestEdge) {
final TestEdge other = (TestEdge) obj;
return super.equals(obj) && Objects.equals(this.weight, other.weight);
}
return false;
}
@Override
public String toString() {
return toStringHelper(this).add("src", src()).add("dst", dst()).
add("weight", weight).toString();
}
}
package org.onlab.graph;
import java.util.Objects;
/**
* Test vertex.
*/
public class TestVertex implements Vertex {
private final String name;
public TestVertex(String name) {
this.name = name;
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof TestVertex) {
final TestVertex other = (TestVertex) obj;
return Objects.equals(this.name, other.name);
}
return false;
}
@Override
public String toString() {
return name;
}
}