blob: 44989392ae4e7c8225830dbf5cfef36435341199 [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 */
tom0633d682014-09-10 12:10:03 -070016package org.onlab.graph;
17
18import java.util.ArrayList;
19import java.util.Collections;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.List;
23import java.util.Map;
24import java.util.Set;
25
26/**
27 * Tarjan algorithm for searching a graph and producing results describing
28 * the graph SCC (strongly-connected components).
29 */
30public class TarjanGraphSearch<V extends Vertex, E extends Edge<V>>
31 implements GraphSearch<V, E> {
32
33 /**
34 * {@inheritDoc}
35 * <p/>
36 * This implementation produces results augmented with information on
37 * SCCs within the graph.
38 * <p/>
39 * To prevent traversal of an edge, the {@link EdgeWeight#weight} should
40 * return a negative value as an edge weight.
41 */
42 @Override
43 public SCCResult<V, E> search(Graph<V, E> graph, EdgeWeight<V, E> weight) {
44 SCCResult<V, E> result = new SCCResult<>(graph);
45 for (V vertex : graph.getVertexes()) {
46 VertexData data = result.data(vertex);
47 if (data == null) {
48 connect(graph, vertex, weight, result);
49 }
50 }
51 return result.build();
52 }
53
54 /**
55 * Scans the specified graph, using recursion, and produces SCC results.
56 *
57 * @param graph graph to search
58 * @param vertex current vertex to scan and connect
59 * @param weight optional edge weight
60 * @param result graph search result
61 * @return augmentation vertexData for the current vertex
62 */
63 private VertexData<V> connect(Graph<V, E> graph, V vertex,
64 EdgeWeight<V, E> weight,
65 SCCResult<V, E> result) {
66 VertexData<V> data = result.addData(vertex);
67
68 // Scan through all egress edges of the current vertex.
69 for (E edge : graph.getEdgesFrom(vertex)) {
70 V nextVertex = edge.dst();
71
72 // If edge weight is negative, skip it.
73 if (weight != null && weight.weight(edge) < 0) {
74 continue;
75 }
76
77 // Attempt to get the augmentation vertexData for the next vertex.
78 VertexData<V> nextData = result.data(nextVertex);
79 if (nextData == null) {
80 // Next vertex has not been visited yet, so do this now.
81 nextData = connect(graph, nextVertex, weight, result);
82 data.lowLink = Math.min(data.lowLink, nextData.lowLink);
83
84 } else if (result.visited(nextData)) {
85 // Next vertex has been visited, which means it is in the
86 // same cluster as the current vertex.
87 data.lowLink = Math.min(data.lowLink, nextData.index);
88 }
89 }
90
91 if (data.lowLink == data.index) {
92 result.addCluster(data);
93 }
94 return data;
95 }
96
97 /**
98 * Graph search result augmented with SCC vertexData.
99 */
100 public static final class SCCResult<V extends Vertex, E extends Edge<V>>
101 implements Result {
102
103 private final Graph<V, E> graph;
104 private List<Set<V>> clusterVertexes = new ArrayList<>();
105 private List<Set<E>> clusterEdges = new ArrayList<>();
106
107 private int index = 0;
108 private final Map<V, VertexData<V>> vertexData = new HashMap<>();
109 private final List<VertexData<V>> visited = new ArrayList<>();
110
111 private SCCResult(Graph<V, E> graph) {
112 this.graph = graph;
113 }
114
115 /**
116 * Returns the number of SCC clusters in the graph.
117 *
118 * @return number of clusters
119 */
120 public int clusterCount() {
121 return clusterEdges.size();
122 }
123
124 /**
125 * Returns the list of strongly connected vertex clusters.
126 *
127 * @return list of strongly connected vertex sets
128 */
129 public List<Set<V>> clusterVertexes() {
130 return clusterVertexes;
131 }
132
133 /**
134 * Returns the list of edges linking strongly connected vertex clusters.
135 *
136 * @return list of strongly connected edge sets
137 */
138 public List<Set<E>> clusterEdges() {
139 return clusterEdges;
140 }
141
142 // Gets the augmentation vertexData for the specified vertex
143 private VertexData<V> data(V vertex) {
144 return vertexData.get(vertex);
145 }
146
147 // Adds augmentation vertexData for the specified vertex
148 private VertexData<V> addData(V vertex) {
149 VertexData<V> d = new VertexData<>(vertex, index);
150 vertexData.put(vertex, d);
151 visited.add(0, d);
152 index++;
153 return d;
154 }
155
156 // Indicates whether the given vertex has been visited
157 private boolean visited(VertexData data) {
158 return visited.contains(data);
159 }
160
161 // Adds a new cluster for the specified vertex
162 private void addCluster(VertexData data) {
163 Set<V> vertexes = findClusterVertices(data);
164 clusterVertexes.add(vertexes);
165 clusterEdges.add(findClusterEdges(vertexes));
166 }
167
168 private Set<V> findClusterVertices(VertexData data) {
169 VertexData<V> nextVertexData;
170 Set<V> vertexes = new HashSet<>();
171 do {
172 nextVertexData = visited.remove(0);
173 vertexes.add(nextVertexData.vertex);
174 } while (data != nextVertexData);
175 return Collections.unmodifiableSet(vertexes);
176 }
177
178 private Set<E> findClusterEdges(Set<V> vertexes) {
179 Set<E> edges = new HashSet<>();
180 for (V vertex : vertexes) {
181 for (E edge : graph.getEdgesFrom(vertex)) {
182 if (vertexes.contains((edge.dst()))) {
183 edges.add(edge);
184 }
185 }
186 }
187 return Collections.unmodifiableSet(edges);
188 }
189
190 public SCCResult<V, E> build() {
191 clusterVertexes = Collections.unmodifiableList(clusterVertexes);
192 clusterEdges = Collections.unmodifiableList(clusterEdges);
193 return this;
194 }
195 }
196
197 // Augments the vertex to assist in determining SCC clusters.
198 private static final class VertexData<V extends Vertex> {
199 final V vertex;
200 int index;
201 int lowLink;
202
203 private VertexData(V vertex, int index) {
204 this.vertex = vertex;
205 this.index = index;
206 this.lowLink = index;
207 }
208 }
209
210}