blob: 94cb60963d8061a2a0ef0f1c1b3244c76dfb4a8d [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present 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}
Thomas Vachuska7b652ad2014-10-30 14:10:51 -070035 * <p>
tom0633d682014-09-10 12:10:03 -070036 * This implementation produces results augmented with information on
37 * SCCs within the graph.
Thomas Vachuska7b652ad2014-10-30 14:10:51 -070038 * </p>
39 * <p>
tom0633d682014-09-10 12:10:03 -070040 * To prevent traversal of an edge, the {@link EdgeWeight#weight} should
41 * return a negative value as an edge weight.
Thomas Vachuska7b652ad2014-10-30 14:10:51 -070042 * </p>
tom0633d682014-09-10 12:10:03 -070043 */
44 @Override
Jonathan Hartd9df7bd2015-11-10 17:10:25 -080045 public SccResult<V, E> search(Graph<V, E> graph, EdgeWeight<V, E> weight) {
46 SccResult<V, E> result = new SccResult<>(graph);
tom0633d682014-09-10 12:10:03 -070047 for (V vertex : graph.getVertexes()) {
48 VertexData data = result.data(vertex);
49 if (data == null) {
50 connect(graph, vertex, weight, result);
51 }
52 }
53 return result.build();
54 }
55
56 /**
57 * Scans the specified graph, using recursion, and produces SCC results.
58 *
59 * @param graph graph to search
60 * @param vertex current vertex to scan and connect
61 * @param weight optional edge weight
62 * @param result graph search result
63 * @return augmentation vertexData for the current vertex
64 */
65 private VertexData<V> connect(Graph<V, E> graph, V vertex,
66 EdgeWeight<V, E> weight,
Jonathan Hartd9df7bd2015-11-10 17:10:25 -080067 SccResult<V, E> result) {
tom0633d682014-09-10 12:10:03 -070068 VertexData<V> data = result.addData(vertex);
69
70 // Scan through all egress edges of the current vertex.
71 for (E edge : graph.getEdgesFrom(vertex)) {
72 V nextVertex = edge.dst();
73
74 // If edge weight is negative, skip it.
75 if (weight != null && weight.weight(edge) < 0) {
76 continue;
77 }
78
79 // Attempt to get the augmentation vertexData for the next vertex.
80 VertexData<V> nextData = result.data(nextVertex);
81 if (nextData == null) {
82 // Next vertex has not been visited yet, so do this now.
83 nextData = connect(graph, nextVertex, weight, result);
84 data.lowLink = Math.min(data.lowLink, nextData.lowLink);
85
86 } else if (result.visited(nextData)) {
87 // Next vertex has been visited, which means it is in the
88 // same cluster as the current vertex.
89 data.lowLink = Math.min(data.lowLink, nextData.index);
90 }
91 }
92
93 if (data.lowLink == data.index) {
94 result.addCluster(data);
95 }
96 return data;
97 }
98
99 /**
100 * Graph search result augmented with SCC vertexData.
101 */
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800102 public static final class SccResult<V extends Vertex, E extends Edge<V>>
tom0633d682014-09-10 12:10:03 -0700103 implements Result {
104
105 private final Graph<V, E> graph;
106 private List<Set<V>> clusterVertexes = new ArrayList<>();
107 private List<Set<E>> clusterEdges = new ArrayList<>();
108
109 private int index = 0;
110 private final Map<V, VertexData<V>> vertexData = new HashMap<>();
111 private final List<VertexData<V>> visited = new ArrayList<>();
112
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800113 private SccResult(Graph<V, E> graph) {
tom0633d682014-09-10 12:10:03 -0700114 this.graph = graph;
115 }
116
117 /**
118 * Returns the number of SCC clusters in the graph.
119 *
120 * @return number of clusters
121 */
122 public int clusterCount() {
123 return clusterEdges.size();
124 }
125
126 /**
127 * Returns the list of strongly connected vertex clusters.
128 *
129 * @return list of strongly connected vertex sets
130 */
131 public List<Set<V>> clusterVertexes() {
132 return clusterVertexes;
133 }
134
135 /**
136 * Returns the list of edges linking strongly connected vertex clusters.
137 *
138 * @return list of strongly connected edge sets
139 */
140 public List<Set<E>> clusterEdges() {
141 return clusterEdges;
142 }
143
144 // Gets the augmentation vertexData for the specified vertex
145 private VertexData<V> data(V vertex) {
146 return vertexData.get(vertex);
147 }
148
149 // Adds augmentation vertexData for the specified vertex
150 private VertexData<V> addData(V vertex) {
151 VertexData<V> d = new VertexData<>(vertex, index);
152 vertexData.put(vertex, d);
153 visited.add(0, d);
154 index++;
155 return d;
156 }
157
158 // Indicates whether the given vertex has been visited
159 private boolean visited(VertexData data) {
160 return visited.contains(data);
161 }
162
163 // Adds a new cluster for the specified vertex
164 private void addCluster(VertexData data) {
165 Set<V> vertexes = findClusterVertices(data);
166 clusterVertexes.add(vertexes);
167 clusterEdges.add(findClusterEdges(vertexes));
168 }
169
170 private Set<V> findClusterVertices(VertexData data) {
171 VertexData<V> nextVertexData;
172 Set<V> vertexes = new HashSet<>();
173 do {
174 nextVertexData = visited.remove(0);
175 vertexes.add(nextVertexData.vertex);
176 } while (data != nextVertexData);
177 return Collections.unmodifiableSet(vertexes);
178 }
179
180 private Set<E> findClusterEdges(Set<V> vertexes) {
181 Set<E> edges = new HashSet<>();
182 for (V vertex : vertexes) {
183 for (E edge : graph.getEdgesFrom(vertex)) {
184 if (vertexes.contains((edge.dst()))) {
185 edges.add(edge);
186 }
187 }
188 }
189 return Collections.unmodifiableSet(edges);
190 }
191
Jonathan Hartd9df7bd2015-11-10 17:10:25 -0800192 public SccResult<V, E> build() {
tom0633d682014-09-10 12:10:03 -0700193 clusterVertexes = Collections.unmodifiableList(clusterVertexes);
194 clusterEdges = Collections.unmodifiableList(clusterEdges);
195 return this;
196 }
197 }
198
199 // Augments the vertex to assist in determining SCC clusters.
200 private static final class VertexData<V extends Vertex> {
201 final V vertex;
202 int index;
203 int lowLink;
204
205 private VertexData(V vertex, int index) {
206 this.vertex = vertex;
207 this.index = index;
208 this.lowLink = index;
209 }
210 }
211
212}