package de.tum.in.www1.jReto.routing.algorithm; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.jgrapht.EdgeFactory; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DefaultWeightedEdge; import org.jgrapht.graph.DirectedMultigraph; import org.jgrapht.graph.DirectedWeightedPseudograph; import org.jgrapht.traverse.ClosestFirstIterator; public class MinimumSteinerTreeApproximation<T> { /** * Computes an approximation of a directed minimum steiner tree. * @param graph The graph of which the minimum steiner tree should be computed * @param startVertex The vertex at which the directed minimum steiner tree should be rooted. * @param vertices A set of vertices that should be included in the minimum steiner tree. * @return A tree representation of the computed tree. */ public static <V, E> Tree<V> approximateSteinerTree(DirectedWeightedPseudograph<V, E> graph, V startVertex, Set<V> vertices) { // 1. Construct the metric closure for the given set of vertices. DirectedWeightedPseudograph<V, MetricClosureEdge<V>> metricClosure = createMetricClosure(graph, vertices); // 2. Compute the edges that compose an aborescence of the metric closure (aka. directed minimum spanning tree). Set<MetricClosureEdge<V>> arborescenceEdges = minimumArborescenceEdges(metricClosure, startVertex); // 3. Reduce the metric closure by removing all edges not computed before. metricClosure.removeAllEdges(inverseEdgeSet(metricClosure, arborescenceEdges)); // 4. Reconstruct a graph containing all vertices of the original graph. DirectedMultigraph<V, DefaultEdge> steinerTreeGraphApproximation = reconstructGraphFromMetricClosure(metricClosure); // 5. Construct a tree representation. Tree<V> steinerTree = constructSteinerTreeApproximation(startVertex, steinerTreeGraphApproximation); return steinerTree; } static class MetricClosureEdge<T> extends DefaultWeightedEdge { private static final long serialVersionUID = 1L; public final List<T> path; public MetricClosureEdge(List<T> path) { this.path = path; } public String toString() { return this.path.toString() + " // "+this.getWeight(); } } static <V,E> Set<E> inverseEdgeSet(DirectedWeightedPseudograph<V,E> graph, Set<E> edgeSet) { Set<E> inversedEdgeSet = new HashSet<>(graph.edgeSet()); inversedEdgeSet.removeAll(edgeSet); return inversedEdgeSet; } /** * Constructs a Tree representation from this graph, by exploring it recursively. May only be called on a graph that does not contain cycles. * @param startVertex The vertex at which to start exploring the graph. * @param graph The graph that should be explored. * @return A Tree representing the explored graph. */ static <T> Tree<T> constructSteinerTreeApproximation(T vertex, DirectedMultigraph<T, DefaultEdge> graph) { Set<Tree<T>> children = new HashSet<>(); if (!graph.containsVertex(vertex)) return null; for (DefaultEdge edge : graph.outgoingEdgesOf(vertex)) { T target = graph.getEdgeTarget(edge); children.add(constructSteinerTreeApproximation(target, graph)); } return new Tree<T>(vertex, children); } static <V, E> Set<E> minimumArborescenceEdges(DirectedWeightedPseudograph<V, E> graph, V startVertex) { // Prim's algorithm ClosestFirstIterator<V, E> iterator = new ClosestFirstIterator<V, E>(graph, startVertex); iterator.setCrossComponentTraversal(false); while (iterator.hasNext()) iterator.next(); Set<E> minimumSpanningTreeEdges = new HashSet<>(); for (V vertex : graph.vertexSet()) { E edge = iterator.getSpanningTreeEdge(vertex); if (edge != null) minimumSpanningTreeEdges.add(edge); } return minimumSpanningTreeEdges; } static <T> void eliminateVertex(DirectedWeightedPseudograph<T, MetricClosureEdge<T>> graph, T vertex) { Set<MetricClosureEdge<T>> incomingEdges = new HashSet<>(graph.incomingEdgesOf(vertex)); Set<MetricClosureEdge<T>> outgoingEdges = new HashSet<>(graph.outgoingEdgesOf(vertex)); for (MetricClosureEdge<T> incomingEdge : incomingEdges) { for (MetricClosureEdge<T> outgoingEdge : outgoingEdges) { List<T> path = new ArrayList<>(); path.addAll(incomingEdge.path); path.add(vertex); path.addAll(outgoingEdge.path); MetricClosureEdge<T> newEdge = new MetricClosureEdge<>(path); graph.addEdge(graph.getEdgeSource(incomingEdge), graph.getEdgeTarget(outgoingEdge), newEdge); graph.setEdgeWeight(newEdge, graph.getEdgeWeight(incomingEdge) + graph.getEdgeWeight(outgoingEdge)); } } graph.removeVertex(vertex); } /** * Computes the metric closure with a given set of vertices over a graph. * @param originalGraph The Graph over which the metric closure should be computed. * @param destinations A set of vertices which should exist in the metric closure. * @return A Graph that uses MetricClosureEdgeAnnotations that store the paths in the original graph to allow later reconstruction. */ public static <T, E> DirectedWeightedPseudograph<T, MetricClosureEdge<T>> createMetricClosure(DirectedWeightedPseudograph<T, E> originalGraph, Set<T> destinations) { DirectedWeightedPseudograph<T, MetricClosureEdge<T>> metricClosure = new DirectedWeightedPseudograph<>(new EdgeFactory<T, MetricClosureEdge<T>>() { @Override public MetricClosureEdge<T> createEdge(T sourceVertex, T targetVertex) { return new MetricClosureEdge<>(new ArrayList<T>()); } }); for (E edge : originalGraph.edgeSet()) { T start = originalGraph.getEdgeSource(edge); T end = originalGraph.getEdgeTarget(edge); metricClosure.addVertex(start); metricClosure.addVertex(end); MetricClosureEdge<T> newEdge = metricClosure.addEdge(start, end); double edgeWeight = originalGraph.getEdgeWeight(edge); metricClosure.setEdgeWeight(newEdge, edgeWeight); } Set<T> removedVertices = new HashSet<T>(originalGraph.vertexSet()); removedVertices.removeAll(destinations); for (T removedVertex : removedVertices) { eliminateVertex(metricClosure, removedVertex); } return metricClosure; } /** * Reconstructs the original graph from a metric closure (i.e. a graph that contains all vertices that were used in the edges of the metric closure). * @param metricClosure a metric closure * @return The reconstructed graph */ public static <T> DirectedMultigraph<T, DefaultEdge> reconstructGraphFromMetricClosure(DirectedWeightedPseudograph<T, MetricClosureEdge<T>> metricClosure) { DirectedMultigraph<T, DefaultEdge> reconstructedGraph = new DirectedMultigraph<>(DefaultEdge.class); for (MetricClosureEdge<T> edge : metricClosure.edgeSet()) { T previousVertex = metricClosure.getEdgeSource(edge); T endVertex = metricClosure.getEdgeTarget(edge); reconstructedGraph.addVertex(previousVertex); reconstructedGraph.addVertex(endVertex); for (T subedge : edge.path) { if (reconstructedGraph.addVertex(subedge)) { reconstructedGraph.addEdge(previousVertex, subedge); } previousVertex = subedge; } reconstructedGraph.addEdge(previousVertex, endVertex); } return reconstructedGraph; } }