blob: 01e73b327c9aa67b3d248384a49e9e771b446e46 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
tom0633d682014-09-10 12:10:03 -070019package org.onlab.graph;
20
21import java.util.ArrayList;
22import java.util.Collections;
23import java.util.HashMap;
24import java.util.HashSet;
25import java.util.List;
26import java.util.Map;
27import java.util.Set;
28
29/**
30 * Tarjan algorithm for searching a graph and producing results describing
31 * the graph SCC (strongly-connected components).
32 */
33public class TarjanGraphSearch<V extends Vertex, E extends Edge<V>>
34 implements GraphSearch<V, E> {
35
36 /**
37 * {@inheritDoc}
38 * <p/>
39 * This implementation produces results augmented with information on
40 * SCCs within the graph.
41 * <p/>
42 * To prevent traversal of an edge, the {@link EdgeWeight#weight} should
43 * return a negative value as an edge weight.
44 */
45 @Override
46 public SCCResult<V, E> search(Graph<V, E> graph, EdgeWeight<V, E> weight) {
47 SCCResult<V, E> result = new SCCResult<>(graph);
48 for (V vertex : graph.getVertexes()) {
49 VertexData data = result.data(vertex);
50 if (data == null) {
51 connect(graph, vertex, weight, result);
52 }
53 }
54 return result.build();
55 }
56
57 /**
58 * Scans the specified graph, using recursion, and produces SCC results.
59 *
60 * @param graph graph to search
61 * @param vertex current vertex to scan and connect
62 * @param weight optional edge weight
63 * @param result graph search result
64 * @return augmentation vertexData for the current vertex
65 */
66 private VertexData<V> connect(Graph<V, E> graph, V vertex,
67 EdgeWeight<V, E> weight,
68 SCCResult<V, E> result) {
69 VertexData<V> data = result.addData(vertex);
70
71 // Scan through all egress edges of the current vertex.
72 for (E edge : graph.getEdgesFrom(vertex)) {
73 V nextVertex = edge.dst();
74
75 // If edge weight is negative, skip it.
76 if (weight != null && weight.weight(edge) < 0) {
77 continue;
78 }
79
80 // Attempt to get the augmentation vertexData for the next vertex.
81 VertexData<V> nextData = result.data(nextVertex);
82 if (nextData == null) {
83 // Next vertex has not been visited yet, so do this now.
84 nextData = connect(graph, nextVertex, weight, result);
85 data.lowLink = Math.min(data.lowLink, nextData.lowLink);
86
87 } else if (result.visited(nextData)) {
88 // Next vertex has been visited, which means it is in the
89 // same cluster as the current vertex.
90 data.lowLink = Math.min(data.lowLink, nextData.index);
91 }
92 }
93
94 if (data.lowLink == data.index) {
95 result.addCluster(data);
96 }
97 return data;
98 }
99
100 /**
101 * Graph search result augmented with SCC vertexData.
102 */
103 public static final class SCCResult<V extends Vertex, E extends Edge<V>>
104 implements Result {
105
106 private final Graph<V, E> graph;
107 private List<Set<V>> clusterVertexes = new ArrayList<>();
108 private List<Set<E>> clusterEdges = new ArrayList<>();
109
110 private int index = 0;
111 private final Map<V, VertexData<V>> vertexData = new HashMap<>();
112 private final List<VertexData<V>> visited = new ArrayList<>();
113
114 private SCCResult(Graph<V, E> graph) {
115 this.graph = graph;
116 }
117
118 /**
119 * Returns the number of SCC clusters in the graph.
120 *
121 * @return number of clusters
122 */
123 public int clusterCount() {
124 return clusterEdges.size();
125 }
126
127 /**
128 * Returns the list of strongly connected vertex clusters.
129 *
130 * @return list of strongly connected vertex sets
131 */
132 public List<Set<V>> clusterVertexes() {
133 return clusterVertexes;
134 }
135
136 /**
137 * Returns the list of edges linking strongly connected vertex clusters.
138 *
139 * @return list of strongly connected edge sets
140 */
141 public List<Set<E>> clusterEdges() {
142 return clusterEdges;
143 }
144
145 // Gets the augmentation vertexData for the specified vertex
146 private VertexData<V> data(V vertex) {
147 return vertexData.get(vertex);
148 }
149
150 // Adds augmentation vertexData for the specified vertex
151 private VertexData<V> addData(V vertex) {
152 VertexData<V> d = new VertexData<>(vertex, index);
153 vertexData.put(vertex, d);
154 visited.add(0, d);
155 index++;
156 return d;
157 }
158
159 // Indicates whether the given vertex has been visited
160 private boolean visited(VertexData data) {
161 return visited.contains(data);
162 }
163
164 // Adds a new cluster for the specified vertex
165 private void addCluster(VertexData data) {
166 Set<V> vertexes = findClusterVertices(data);
167 clusterVertexes.add(vertexes);
168 clusterEdges.add(findClusterEdges(vertexes));
169 }
170
171 private Set<V> findClusterVertices(VertexData data) {
172 VertexData<V> nextVertexData;
173 Set<V> vertexes = new HashSet<>();
174 do {
175 nextVertexData = visited.remove(0);
176 vertexes.add(nextVertexData.vertex);
177 } while (data != nextVertexData);
178 return Collections.unmodifiableSet(vertexes);
179 }
180
181 private Set<E> findClusterEdges(Set<V> vertexes) {
182 Set<E> edges = new HashSet<>();
183 for (V vertex : vertexes) {
184 for (E edge : graph.getEdgesFrom(vertex)) {
185 if (vertexes.contains((edge.dst()))) {
186 edges.add(edge);
187 }
188 }
189 }
190 return Collections.unmodifiableSet(edges);
191 }
192
193 public SCCResult<V, E> build() {
194 clusterVertexes = Collections.unmodifiableList(clusterVertexes);
195 clusterEdges = Collections.unmodifiableList(clusterEdges);
196 return this;
197 }
198 }
199
200 // Augments the vertex to assist in determining SCC clusters.
201 private static final class VertexData<V extends Vertex> {
202 final V vertex;
203 int index;
204 int lowLink;
205
206 private VertexData(V vertex, int index) {
207 this.vertex = vertex;
208 this.index = index;
209 this.lowLink = index;
210 }
211 }
212
213}