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