001/* 002 * Copyright (C) 2014 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.graph; 018 019import static com.google.common.base.Preconditions.checkArgument; 020import static com.google.common.graph.GraphConstants.NODE_NOT_IN_GRAPH; 021 022import com.google.common.annotations.Beta; 023import com.google.common.annotations.GwtIncompatible; 024import com.google.common.base.Objects; 025import com.google.common.collect.Iterables; 026import com.google.common.collect.Maps; 027import com.google.errorprone.annotations.CanIgnoreReturnValue; 028import java.util.ArrayDeque; 029import java.util.Collection; 030import java.util.Collections; 031import java.util.HashSet; 032import java.util.LinkedHashSet; 033import java.util.Map; 034import java.util.Queue; 035import java.util.Set; 036import javax.annotation.Nullable; 037 038/** 039 * Static utility methods for {@link Graph}, {@link ValueGraph}, and {@link Network} instances. 040 * 041 * @author James Sexton 042 * @author Joshua O'Madadhain 043 * @since 20.0 044 */ 045@Beta 046public final class Graphs { 047 048 private Graphs() {} 049 050 // Graph query methods 051 052 /** 053 * Returns true if {@code graph} has at least one cycle. A cycle is defined as a non-empty subset 054 * of edges in a graph arranged to form a path (a sequence of adjacent outgoing edges) starting 055 * and ending with the same node. 056 * 057 * <p>This method will detect any non-empty cycle, including self-loops (a cycle of length 1). 058 */ 059 public static <N> boolean hasCycle(Graph<N> graph) { 060 int numEdges = graph.edges().size(); 061 if (numEdges == 0) { 062 return false; // An edge-free graph is acyclic by definition. 063 } 064 if (!graph.isDirected() && numEdges >= graph.nodes().size()) { 065 return true; // Optimization for the undirected case: at least one cycle must exist. 066 } 067 068 Map<Object, NodeVisitState> visitedNodes = 069 Maps.newHashMapWithExpectedSize(graph.nodes().size()); 070 for (N node : graph.nodes()) { 071 if (subgraphHasCycle(graph, visitedNodes, node, null)) { 072 return true; 073 } 074 } 075 return false; 076 } 077 078 /** 079 * Returns true if {@code network} has at least one cycle. A cycle is defined as a non-empty 080 * subset of edges in a graph arranged to form a path (a sequence of adjacent outgoing edges) 081 * starting and ending with the same node. 082 * 083 * <p>This method will detect any non-empty cycle, including self-loops (a cycle of length 1). 084 */ 085 public static boolean hasCycle(Network<?, ?> network) { 086 // In a directed graph, parallel edges cannot introduce a cycle in an acyclic graph. 087 // However, in an undirected graph, any parallel edge induces a cycle in the graph. 088 if (!network.isDirected() 089 && network.allowsParallelEdges() 090 && network.edges().size() > network.asGraph().edges().size()) { 091 return true; 092 } 093 return hasCycle(network.asGraph()); 094 } 095 096 /** 097 * Performs a traversal of the nodes reachable from {@code node}. If we ever reach a node we've 098 * already visited (following only outgoing edges and without reusing edges), we know there's a 099 * cycle in the graph. 100 */ 101 private static <N> boolean subgraphHasCycle( 102 Graph<N> graph, Map<Object, NodeVisitState> visitedNodes, N node, @Nullable N previousNode) { 103 NodeVisitState state = visitedNodes.get(node); 104 if (state == NodeVisitState.COMPLETE) { 105 return false; 106 } 107 if (state == NodeVisitState.PENDING) { 108 return true; 109 } 110 111 visitedNodes.put(node, NodeVisitState.PENDING); 112 for (N nextNode : graph.successors(node)) { 113 if (canTraverseWithoutReusingEdge(graph, nextNode, previousNode) 114 && subgraphHasCycle(graph, visitedNodes, nextNode, node)) { 115 return true; 116 } 117 } 118 visitedNodes.put(node, NodeVisitState.COMPLETE); 119 return false; 120 } 121 122 /** 123 * Determines whether an edge has already been used during traversal. In the directed case a cycle 124 * is always detected before reusing an edge, so no special logic is required. In the undirected 125 * case, we must take care not to "backtrack" over an edge (i.e. going from A to B and then going 126 * from B to A). 127 */ 128 private static boolean canTraverseWithoutReusingEdge( 129 Graph<?> graph, Object nextNode, @Nullable Object previousNode) { 130 if (graph.isDirected() || !Objects.equal(previousNode, nextNode)) { 131 return true; 132 } 133 // This falls into the undirected A->B->A case. The Graph interface does not support parallel 134 // edges, so this traversal would require reusing the undirected AB edge. 135 return false; 136 } 137 138 /** 139 * Returns the transitive closure of {@code graph}. The transitive closure of a graph is another 140 * graph with an edge connecting node A to node B if node B is {@link #reachableNodes(Graph, 141 * Object) reachable} from node A. 142 * 143 * <p>This is a "snapshot" based on the current topology of {@code graph}, rather than a live view 144 * of the transitive closure of {@code graph}. In other words, the returned {@link Graph} will not 145 * be updated after modifications to {@code graph}. 146 */ 147 // TODO(b/31438252): Consider potential optimizations for this algorithm. 148 public static <N> Graph<N> transitiveClosure(Graph<N> graph) { 149 MutableGraph<N> transitiveClosure = GraphBuilder.from(graph).allowsSelfLoops(true).build(); 150 // Every node is, at a minimum, reachable from itself. Since the resulting transitive closure 151 // will have no isolated nodes, we can skip adding nodes explicitly and let putEdge() do it. 152 153 if (graph.isDirected()) { 154 // Note: works for both directed and undirected graphs, but we only use in the directed case. 155 for (N node : graph.nodes()) { 156 for (N reachableNode : reachableNodes(graph, node)) { 157 transitiveClosure.putEdge(node, reachableNode); 158 } 159 } 160 } else { 161 // An optimization for the undirected case: for every node B reachable from node A, 162 // node A and node B have the same reachability set. 163 Set<N> visitedNodes = new HashSet<N>(); 164 for (N node : graph.nodes()) { 165 if (!visitedNodes.contains(node)) { 166 Set<N> reachableNodes = reachableNodes(graph, node); 167 visitedNodes.addAll(reachableNodes); 168 int pairwiseMatch = 1; // start at 1 to include self-loops 169 for (N nodeU : reachableNodes) { 170 for (N nodeV : Iterables.limit(reachableNodes, pairwiseMatch++)) { 171 transitiveClosure.putEdge(nodeU, nodeV); 172 } 173 } 174 } 175 } 176 } 177 178 return transitiveClosure; 179 } 180 181 /** 182 * Returns the set of nodes that are reachable from {@code node}. Node B is defined as reachable 183 * from node A if there exists a path (a sequence of adjacent outgoing edges) starting at node A 184 * and ending at node B. Note that a node is always reachable from itself via a zero-length path. 185 * 186 * <p>This is a "snapshot" based on the current topology of {@code graph}, rather than a live view 187 * of the set of nodes reachable from {@code node}. In other words, the returned {@link Set} will 188 * not be updated after modifications to {@code graph}. 189 * 190 * @throws IllegalArgumentException if {@code node} is not present in {@code graph} 191 */ 192 public static <N> Set<N> reachableNodes(Graph<N> graph, N node) { 193 checkArgument(graph.nodes().contains(node), NODE_NOT_IN_GRAPH, node); 194 Set<N> visitedNodes = new LinkedHashSet<N>(); 195 Queue<N> queuedNodes = new ArrayDeque<N>(); 196 visitedNodes.add(node); 197 queuedNodes.add(node); 198 // Perform a breadth-first traversal rooted at the input node. 199 while (!queuedNodes.isEmpty()) { 200 N currentNode = queuedNodes.remove(); 201 for (N successor : graph.successors(currentNode)) { 202 if (visitedNodes.add(successor)) { 203 queuedNodes.add(successor); 204 } 205 } 206 } 207 return Collections.unmodifiableSet(visitedNodes); 208 } 209 210 /** 211 * @deprecated Use {@link Graph#equals(Object)} instead. This method will be removed in late 2017. 212 */ 213 // TODO(user): Delete this method. 214 @Deprecated 215 public static boolean equivalent(@Nullable Graph<?> graphA, @Nullable Graph<?> graphB) { 216 return Objects.equal(graphA, graphB); 217 } 218 219 /** 220 * @deprecated Use {@link ValueGraph#equals(Object)} instead. This method will be removed in late 221 * 2017. 222 */ 223 // TODO(user): Delete this method. 224 @Deprecated 225 public static boolean equivalent( 226 @Nullable ValueGraph<?, ?> graphA, @Nullable ValueGraph<?, ?> graphB) { 227 return Objects.equal(graphA, graphB); 228 } 229 230 /** 231 * @deprecated Use {@link Network#equals(Object)} instead. This method will be removed in late 232 * 2017. 233 */ 234 // TODO(user): Delete this method. 235 @Deprecated 236 public static boolean equivalent( 237 @Nullable Network<?, ?> networkA, @Nullable Network<?, ?> networkB) { 238 return Objects.equal(networkA, networkB); 239 } 240 241 // Graph mutation methods 242 243 // Graph view methods 244 245 /** 246 * Returns a view of {@code graph} with the direction (if any) of every edge reversed. All other 247 * properties remain intact, and further updates to {@code graph} will be reflected in the view. 248 */ 249 public static <N> Graph<N> transpose(Graph<N> graph) { 250 if (!graph.isDirected()) { 251 return graph; // the transpose of an undirected graph is an identical graph 252 } 253 254 if (graph instanceof TransposedGraph) { 255 return ((TransposedGraph<N>) graph).graph; 256 } 257 258 return new TransposedGraph<N>(graph); 259 } 260 261 // NOTE: this should work as long as the delegate graph's implementation of edges() (like that of 262 // AbstractGraph) derives its behavior from calling successors(). 263 private static class TransposedGraph<N> extends ForwardingGraph<N> { 264 private final Graph<N> graph; 265 266 TransposedGraph(Graph<N> graph) { 267 this.graph = graph; 268 } 269 270 @Override 271 protected Graph<N> delegate() { 272 return graph; 273 } 274 275 @Override 276 public Set<N> predecessors(N node) { 277 return delegate().successors(node); // transpose 278 } 279 280 @Override 281 public Set<N> successors(N node) { 282 return delegate().predecessors(node); // transpose 283 } 284 285 @Override 286 public int inDegree(N node) { 287 return delegate().outDegree(node); // transpose 288 } 289 290 @Override 291 public int outDegree(N node) { 292 return delegate().inDegree(node); // transpose 293 } 294 295 @Override 296 public boolean hasEdgeConnecting(N nodeU, N nodeV) { 297 return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose 298 } 299 } 300 301 /** 302 * Returns a view of {@code graph} with the direction (if any) of every edge reversed. All other 303 * properties remain intact, and further updates to {@code graph} will be reflected in the view. 304 */ 305 public static <N, V> ValueGraph<N, V> transpose(ValueGraph<N, V> graph) { 306 if (!graph.isDirected()) { 307 return graph; // the transpose of an undirected graph is an identical graph 308 } 309 310 if (graph instanceof TransposedValueGraph) { 311 return ((TransposedValueGraph<N, V>) graph).graph; 312 } 313 314 return new TransposedValueGraph<N, V>(graph); 315 } 316 317 // NOTE: this should work as long as the delegate graph's implementation of edges() (like that of 318 // AbstractValueGraph) derives its behavior from calling successors(). 319 private static class TransposedValueGraph<N, V> extends ForwardingValueGraph<N, V> { 320 private final ValueGraph<N, V> graph; 321 322 TransposedValueGraph(ValueGraph<N, V> graph) { 323 this.graph = graph; 324 } 325 326 @Override 327 protected ValueGraph<N, V> delegate() { 328 return graph; 329 } 330 331 @Override 332 public Set<N> predecessors(N node) { 333 return delegate().successors(node); // transpose 334 } 335 336 @Override 337 public Set<N> successors(N node) { 338 return delegate().predecessors(node); // transpose 339 } 340 341 @Override 342 public int inDegree(N node) { 343 return delegate().outDegree(node); // transpose 344 } 345 346 @Override 347 public int outDegree(N node) { 348 return delegate().inDegree(node); // transpose 349 } 350 351 @Override 352 public boolean hasEdgeConnecting(N nodeU, N nodeV) { 353 return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose 354 } 355 356 @Override 357 @Nullable 358 public V edgeValueOrDefault(N nodeU, N nodeV, @Nullable V defaultValue) { 359 return delegate().edgeValueOrDefault(nodeV, nodeU, defaultValue); // transpose 360 } 361 } 362 363 /** 364 * Returns a view of {@code network} with the direction (if any) of every edge reversed. All other 365 * properties remain intact, and further updates to {@code network} will be reflected in the view. 366 */ 367 @GwtIncompatible 368 public static <N, E> Network<N, E> transpose(Network<N, E> network) { 369 if (!network.isDirected()) { 370 return network; // the transpose of an undirected network is an identical network 371 } 372 373 if (network instanceof TransposedNetwork) { 374 return ((TransposedNetwork<N, E>) network).network; 375 } 376 377 return new TransposedNetwork<N, E>(network); 378 } 379 380 @GwtIncompatible 381 private static class TransposedNetwork<N, E> extends ForwardingNetwork<N, E> { 382 private final Network<N, E> network; 383 384 TransposedNetwork(Network<N, E> network) { 385 this.network = network; 386 } 387 388 @Override 389 protected Network<N, E> delegate() { 390 return network; 391 } 392 393 @Override 394 public Set<N> predecessors(N node) { 395 return delegate().successors(node); // transpose 396 } 397 398 @Override 399 public Set<N> successors(N node) { 400 return delegate().predecessors(node); // transpose 401 } 402 403 @Override 404 public int inDegree(N node) { 405 return delegate().outDegree(node); // transpose 406 } 407 408 @Override 409 public int outDegree(N node) { 410 return delegate().inDegree(node); // transpose 411 } 412 413 @Override 414 public Set<E> inEdges(N node) { 415 return delegate().outEdges(node); // transpose 416 } 417 418 @Override 419 public Set<E> outEdges(N node) { 420 return delegate().inEdges(node); // transpose 421 } 422 423 @Override 424 public EndpointPair<N> incidentNodes(E edge) { 425 EndpointPair<N> endpointPair = delegate().incidentNodes(edge); 426 return EndpointPair.of(network, endpointPair.nodeV(), endpointPair.nodeU()); // transpose 427 } 428 429 @Override 430 public Set<E> edgesConnecting(N nodeU, N nodeV) { 431 return delegate().edgesConnecting(nodeV, nodeU); // transpose 432 } 433 434 @Override 435 public E edgeConnectingOrNull(N nodeU, N nodeV) { 436 return delegate().edgeConnectingOrNull(nodeV, nodeU); // transpose 437 } 438 439 @Override 440 public boolean hasEdgeConnecting(N nodeU, N nodeV) { 441 return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose 442 } 443 } 444 445 // Graph copy methods 446 447 /** 448 * Returns the subgraph of {@code graph} induced by {@code nodes}. This subgraph is a new graph 449 * that contains all of the nodes in {@code nodes}, and all of the {@link Graph#edges() edges} 450 * from {@code graph} for which both nodes are contained by {@code nodes}. 451 * 452 * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph 453 */ 454 public static <N> MutableGraph<N> inducedSubgraph(Graph<N> graph, Iterable<? extends N> nodes) { 455 MutableGraph<N> subgraph = (nodes instanceof Collection) 456 ? GraphBuilder.from(graph).expectedNodeCount(((Collection) nodes).size()).build() 457 : GraphBuilder.from(graph).build(); 458 for (N node : nodes) { 459 subgraph.addNode(node); 460 } 461 for (N node : subgraph.nodes()) { 462 for (N successorNode : graph.successors(node)) { 463 if (subgraph.nodes().contains(successorNode)) { 464 subgraph.putEdge(node, successorNode); 465 } 466 } 467 } 468 return subgraph; 469 } 470 471 /** 472 * Returns the subgraph of {@code graph} induced by {@code nodes}. This subgraph is a new graph 473 * that contains all of the nodes in {@code nodes}, and all of the {@link Graph#edges() edges} 474 * (and associated edge values) from {@code graph} for which both nodes are contained by {@code 475 * nodes}. 476 * 477 * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph 478 */ 479 public static <N, V> MutableValueGraph<N, V> inducedSubgraph( 480 ValueGraph<N, V> graph, Iterable<? extends N> nodes) { 481 MutableValueGraph<N, V> subgraph = (nodes instanceof Collection) 482 ? ValueGraphBuilder.from(graph).expectedNodeCount(((Collection) nodes).size()).build() 483 : ValueGraphBuilder.from(graph).build(); 484 for (N node : nodes) { 485 subgraph.addNode(node); 486 } 487 for (N node : subgraph.nodes()) { 488 for (N successorNode : graph.successors(node)) { 489 if (subgraph.nodes().contains(successorNode)) { 490 subgraph.putEdgeValue( 491 node, successorNode, graph.edgeValueOrDefault(node, successorNode, null)); 492 } 493 } 494 } 495 return subgraph; 496 } 497 498 /** 499 * Returns the subgraph of {@code network} induced by {@code nodes}. This subgraph is a new graph 500 * that contains all of the nodes in {@code nodes}, and all of the {@link Network#edges() edges} 501 * from {@code network} for which the {@link Network#incidentNodes(Object) incident nodes} are 502 * both contained by {@code nodes}. 503 * 504 * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph 505 */ 506 @GwtIncompatible 507 public static <N, E> MutableNetwork<N, E> inducedSubgraph( 508 Network<N, E> network, Iterable<? extends N> nodes) { 509 MutableNetwork<N, E> subgraph = (nodes instanceof Collection) 510 ? NetworkBuilder.from(network).expectedNodeCount(((Collection) nodes).size()).build() 511 : NetworkBuilder.from(network).build(); 512 for (N node : nodes) { 513 subgraph.addNode(node); 514 } 515 for (N node : subgraph.nodes()) { 516 for (E edge : network.outEdges(node)) { 517 N successorNode = network.incidentNodes(edge).adjacentNode(node); 518 if (subgraph.nodes().contains(successorNode)) { 519 subgraph.addEdge(node, successorNode, edge); 520 } 521 } 522 } 523 return subgraph; 524 } 525 526 /** Creates a mutable copy of {@code graph} with the same nodes and edges. */ 527 public static <N> MutableGraph<N> copyOf(Graph<N> graph) { 528 MutableGraph<N> copy = GraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); 529 for (N node : graph.nodes()) { 530 copy.addNode(node); 531 } 532 for (EndpointPair<N> edge : graph.edges()) { 533 copy.putEdge(edge.nodeU(), edge.nodeV()); 534 } 535 return copy; 536 } 537 538 /** Creates a mutable copy of {@code graph} with the same nodes, edges, and edge values. */ 539 public static <N, V> MutableValueGraph<N, V> copyOf(ValueGraph<N, V> graph) { 540 MutableValueGraph<N, V> copy = 541 ValueGraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); 542 for (N node : graph.nodes()) { 543 copy.addNode(node); 544 } 545 for (EndpointPair<N> edge : graph.edges()) { 546 copy.putEdgeValue( 547 edge.nodeU(), edge.nodeV(), graph.edgeValueOrDefault(edge.nodeU(), edge.nodeV(), null)); 548 } 549 return copy; 550 } 551 552 /** Creates a mutable copy of {@code network} with the same nodes and edges. */ 553 @GwtIncompatible 554 public static <N, E> MutableNetwork<N, E> copyOf(Network<N, E> network) { 555 MutableNetwork<N, E> copy = 556 NetworkBuilder.from(network) 557 .expectedNodeCount(network.nodes().size()) 558 .expectedEdgeCount(network.edges().size()) 559 .build(); 560 for (N node : network.nodes()) { 561 copy.addNode(node); 562 } 563 for (E edge : network.edges()) { 564 EndpointPair<N> endpointPair = network.incidentNodes(edge); 565 copy.addEdge(endpointPair.nodeU(), endpointPair.nodeV(), edge); 566 } 567 return copy; 568 } 569 570 @CanIgnoreReturnValue 571 static int checkNonNegative(int value) { 572 checkArgument(value >= 0, "Not true that %s is non-negative.", value); 573 return value; 574 } 575 576 @CanIgnoreReturnValue 577 static int checkPositive(int value) { 578 checkArgument(value > 0, "Not true that %s is positive.", value); 579 return value; 580 } 581 582 @CanIgnoreReturnValue 583 static long checkNonNegative(long value) { 584 checkArgument(value >= 0, "Not true that %s is non-negative.", value); 585 return value; 586 } 587 588 @CanIgnoreReturnValue 589 static long checkPositive(long value) { 590 checkArgument(value > 0, "Not true that %s is positive.", value); 591 return value; 592 } 593 594 /** 595 * An enum representing the state of a node during DFS. {@code PENDING} means that the node is on 596 * the stack of the DFS, while {@code COMPLETE} means that the node and all its successors have 597 * been already explored. Any node that has not been explored will not have a state at all. 598 */ 599 private enum NodeVisitState { 600 PENDING, 601 COMPLETE 602 } 603}