All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.graph.Graphs Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 33.0.2.Final
Show newest version
/*
 * Copyright (C) 2014 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.graph;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.graph.GraphConstants.NODE_NOT_IN_GRAPH;

import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
 * Static utility methods for {@link Graph}, {@link ValueGraph}, and {@link Network} instances.
 *
 * @author James Sexton
 * @author Joshua O'Madadhain
 * @since 20.0
 */
@Beta
public final class Graphs {

  private Graphs() {}

  // Graph query methods

  /**
   * Returns true if {@code graph} has at least one cycle. A cycle is defined as a non-empty subset
   * of edges in a graph arranged to form a path (a sequence of adjacent outgoing edges) starting
   * and ending with the same node.
   *
   * 

This method will detect any non-empty cycle, including self-loops (a cycle of length 1). */ public static boolean hasCycle(Graph graph) { int numEdges = graph.edges().size(); if (numEdges == 0) { return false; // An edge-free graph is acyclic by definition. } if (!graph.isDirected() && numEdges >= graph.nodes().size()) { return true; // Optimization for the undirected case: at least one cycle must exist. } Map visitedNodes = Maps.newHashMapWithExpectedSize(graph.nodes().size()); for (N node : graph.nodes()) { if (subgraphHasCycle(graph, visitedNodes, node, null)) { return true; } } return false; } /** * Returns true if {@code network} has at least one cycle. A cycle is defined as a non-empty * subset of edges in a graph arranged to form a path (a sequence of adjacent outgoing edges) * starting and ending with the same node. * *

This method will detect any non-empty cycle, including self-loops (a cycle of length 1). */ public static boolean hasCycle(Network network) { // In a directed graph, parallel edges cannot introduce a cycle in an acyclic graph. // However, in an undirected graph, any parallel edge induces a cycle in the graph. if (!network.isDirected() && network.allowsParallelEdges() && network.edges().size() > network.asGraph().edges().size()) { return true; } return hasCycle(network.asGraph()); } /** * Performs a traversal of the nodes reachable from {@code node}. If we ever reach a node we've * already visited (following only outgoing edges and without reusing edges), we know there's a * cycle in the graph. */ private static boolean subgraphHasCycle( Graph graph, Map visitedNodes, N node, @NullableDecl N previousNode) { NodeVisitState state = visitedNodes.get(node); if (state == NodeVisitState.COMPLETE) { return false; } if (state == NodeVisitState.PENDING) { return true; } visitedNodes.put(node, NodeVisitState.PENDING); for (N nextNode : graph.successors(node)) { if (canTraverseWithoutReusingEdge(graph, nextNode, previousNode) && subgraphHasCycle(graph, visitedNodes, nextNode, node)) { return true; } } visitedNodes.put(node, NodeVisitState.COMPLETE); return false; } /** * Determines whether an edge has already been used during traversal. In the directed case a cycle * is always detected before reusing an edge, so no special logic is required. In the undirected * case, we must take care not to "backtrack" over an edge (i.e. going from A to B and then going * from B to A). */ private static boolean canTraverseWithoutReusingEdge( Graph graph, Object nextNode, @NullableDecl Object previousNode) { if (graph.isDirected() || !Objects.equal(previousNode, nextNode)) { return true; } // This falls into the undirected A->B->A case. The Graph interface does not support parallel // edges, so this traversal would require reusing the undirected AB edge. return false; } /** * Returns the transitive closure of {@code graph}. The transitive closure of a graph is another * graph with an edge connecting node A to node B if node B is {@link #reachableNodes(Graph, * Object) reachable} from node A. * *

This is a "snapshot" based on the current topology of {@code graph}, rather than a live view * of the transitive closure of {@code graph}. In other words, the returned {@link Graph} will not * be updated after modifications to {@code graph}. */ // TODO(b/31438252): Consider potential optimizations for this algorithm. public static Graph transitiveClosure(Graph graph) { MutableGraph transitiveClosure = GraphBuilder.from(graph).allowsSelfLoops(true).build(); // Every node is, at a minimum, reachable from itself. Since the resulting transitive closure // will have no isolated nodes, we can skip adding nodes explicitly and let putEdge() do it. if (graph.isDirected()) { // Note: works for both directed and undirected graphs, but we only use in the directed case. for (N node : graph.nodes()) { for (N reachableNode : reachableNodes(graph, node)) { transitiveClosure.putEdge(node, reachableNode); } } } else { // An optimization for the undirected case: for every node B reachable from node A, // node A and node B have the same reachability set. Set visitedNodes = new HashSet(); for (N node : graph.nodes()) { if (!visitedNodes.contains(node)) { Set reachableNodes = reachableNodes(graph, node); visitedNodes.addAll(reachableNodes); int pairwiseMatch = 1; // start at 1 to include self-loops for (N nodeU : reachableNodes) { for (N nodeV : Iterables.limit(reachableNodes, pairwiseMatch++)) { transitiveClosure.putEdge(nodeU, nodeV); } } } } } return transitiveClosure; } /** * Returns the set of nodes that are reachable from {@code node}. Node B is defined as reachable * from node A if there exists a path (a sequence of adjacent outgoing edges) starting at node A * and ending at node B. Note that a node is always reachable from itself via a zero-length path. * *

This is a "snapshot" based on the current topology of {@code graph}, rather than a live view * of the set of nodes reachable from {@code node}. In other words, the returned {@link Set} will * not be updated after modifications to {@code graph}. * * @throws IllegalArgumentException if {@code node} is not present in {@code graph} */ public static Set reachableNodes(Graph graph, N node) { checkArgument(graph.nodes().contains(node), NODE_NOT_IN_GRAPH, node); Set visitedNodes = new LinkedHashSet(); Queue queuedNodes = new ArrayDeque(); visitedNodes.add(node); queuedNodes.add(node); // Perform a breadth-first traversal rooted at the input node. while (!queuedNodes.isEmpty()) { N currentNode = queuedNodes.remove(); for (N successor : graph.successors(currentNode)) { if (visitedNodes.add(successor)) { queuedNodes.add(successor); } } } return Collections.unmodifiableSet(visitedNodes); } // Graph mutation methods // Graph view methods /** * Returns a view of {@code graph} with the direction (if any) of every edge reversed. All other * properties remain intact, and further updates to {@code graph} will be reflected in the view. */ public static Graph transpose(Graph graph) { if (!graph.isDirected()) { return graph; // the transpose of an undirected graph is an identical graph } if (graph instanceof TransposedGraph) { return ((TransposedGraph) graph).graph; } return new TransposedGraph(graph); } /** * Returns a view of {@code graph} with the direction (if any) of every edge reversed. All other * properties remain intact, and further updates to {@code graph} will be reflected in the view. */ public static ValueGraph transpose(ValueGraph graph) { if (!graph.isDirected()) { return graph; // the transpose of an undirected graph is an identical graph } if (graph instanceof TransposedValueGraph) { return ((TransposedValueGraph) graph).graph; } return new TransposedValueGraph<>(graph); } /** * Returns a view of {@code network} with the direction (if any) of every edge reversed. All other * properties remain intact, and further updates to {@code network} will be reflected in the view. */ public static Network transpose(Network network) { if (!network.isDirected()) { return network; // the transpose of an undirected network is an identical network } if (network instanceof TransposedNetwork) { return ((TransposedNetwork) network).network; } return new TransposedNetwork<>(network); } // NOTE: this should work as long as the delegate graph's implementation of edges() (like that of // AbstractGraph) derives its behavior from calling successors(). private static class TransposedGraph extends ForwardingGraph { private final Graph graph; TransposedGraph(Graph graph) { this.graph = graph; } @Override protected Graph delegate() { return graph; } @Override public Set predecessors(N node) { return delegate().successors(node); // transpose } @Override public Set successors(N node) { return delegate().predecessors(node); // transpose } @Override public int inDegree(N node) { return delegate().outDegree(node); // transpose } @Override public int outDegree(N node) { return delegate().inDegree(node); // transpose } @Override public boolean hasEdgeConnecting(N nodeU, N nodeV) { return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose } } // NOTE: this should work as long as the delegate graph's implementation of edges() (like that of // AbstractValueGraph) derives its behavior from calling successors(). private static class TransposedValueGraph extends ForwardingValueGraph { private final ValueGraph graph; TransposedValueGraph(ValueGraph graph) { this.graph = graph; } @Override protected ValueGraph delegate() { return graph; } @Override public Set predecessors(N node) { return delegate().successors(node); // transpose } @Override public Set successors(N node) { return delegate().predecessors(node); // transpose } @Override public int inDegree(N node) { return delegate().outDegree(node); // transpose } @Override public int outDegree(N node) { return delegate().inDegree(node); // transpose } @Override public boolean hasEdgeConnecting(N nodeU, N nodeV) { return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose } @Override public Optional edgeValue(N nodeU, N nodeV) { return delegate().edgeValue(nodeV, nodeU); // transpose } @Override @NullableDecl public V edgeValueOrDefault(N nodeU, N nodeV, @NullableDecl V defaultValue) { return delegate().edgeValueOrDefault(nodeV, nodeU, defaultValue); // transpose } } private static class TransposedNetwork extends ForwardingNetwork { private final Network network; TransposedNetwork(Network network) { this.network = network; } @Override protected Network delegate() { return network; } @Override public Set predecessors(N node) { return delegate().successors(node); // transpose } @Override public Set successors(N node) { return delegate().predecessors(node); // transpose } @Override public int inDegree(N node) { return delegate().outDegree(node); // transpose } @Override public int outDegree(N node) { return delegate().inDegree(node); // transpose } @Override public Set inEdges(N node) { return delegate().outEdges(node); // transpose } @Override public Set outEdges(N node) { return delegate().inEdges(node); // transpose } @Override public EndpointPair incidentNodes(E edge) { EndpointPair endpointPair = delegate().incidentNodes(edge); return EndpointPair.of(network, endpointPair.nodeV(), endpointPair.nodeU()); // transpose } @Override public Set edgesConnecting(N nodeU, N nodeV) { return delegate().edgesConnecting(nodeV, nodeU); // transpose } @Override public Optional edgeConnecting(N nodeU, N nodeV) { return delegate().edgeConnecting(nodeV, nodeU); // transpose } @Override public E edgeConnectingOrNull(N nodeU, N nodeV) { return delegate().edgeConnectingOrNull(nodeV, nodeU); // transpose } @Override public boolean hasEdgeConnecting(N nodeU, N nodeV) { return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose } } // Graph copy methods /** * Returns the subgraph of {@code graph} induced by {@code nodes}. This subgraph is a new graph * that contains all of the nodes in {@code nodes}, and all of the {@link Graph#edges() edges} * from {@code graph} for which both nodes are contained by {@code nodes}. * * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph */ public static MutableGraph inducedSubgraph(Graph graph, Iterable nodes) { MutableGraph subgraph = (nodes instanceof Collection) ? GraphBuilder.from(graph).expectedNodeCount(((Collection) nodes).size()).build() : GraphBuilder.from(graph).build(); for (N node : nodes) { subgraph.addNode(node); } for (N node : subgraph.nodes()) { for (N successorNode : graph.successors(node)) { if (subgraph.nodes().contains(successorNode)) { subgraph.putEdge(node, successorNode); } } } return subgraph; } /** * Returns the subgraph of {@code graph} induced by {@code nodes}. This subgraph is a new graph * that contains all of the nodes in {@code nodes}, and all of the {@link Graph#edges() edges} * (and associated edge values) from {@code graph} for which both nodes are contained by {@code * nodes}. * * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph */ public static MutableValueGraph inducedSubgraph( ValueGraph graph, Iterable nodes) { MutableValueGraph subgraph = (nodes instanceof Collection) ? ValueGraphBuilder.from(graph).expectedNodeCount(((Collection) nodes).size()).build() : ValueGraphBuilder.from(graph).build(); for (N node : nodes) { subgraph.addNode(node); } for (N node : subgraph.nodes()) { for (N successorNode : graph.successors(node)) { if (subgraph.nodes().contains(successorNode)) { subgraph.putEdgeValue( node, successorNode, graph.edgeValueOrDefault(node, successorNode, null)); } } } return subgraph; } /** * Returns the subgraph of {@code network} induced by {@code nodes}. This subgraph is a new graph * that contains all of the nodes in {@code nodes}, and all of the {@link Network#edges() edges} * from {@code network} for which the {@link Network#incidentNodes(Object) incident nodes} are * both contained by {@code nodes}. * * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph */ public static MutableNetwork inducedSubgraph( Network network, Iterable nodes) { MutableNetwork subgraph = (nodes instanceof Collection) ? NetworkBuilder.from(network).expectedNodeCount(((Collection) nodes).size()).build() : NetworkBuilder.from(network).build(); for (N node : nodes) { subgraph.addNode(node); } for (N node : subgraph.nodes()) { for (E edge : network.outEdges(node)) { N successorNode = network.incidentNodes(edge).adjacentNode(node); if (subgraph.nodes().contains(successorNode)) { subgraph.addEdge(node, successorNode, edge); } } } return subgraph; } /** Creates a mutable copy of {@code graph} with the same nodes and edges. */ public static MutableGraph copyOf(Graph graph) { MutableGraph copy = GraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); for (N node : graph.nodes()) { copy.addNode(node); } for (EndpointPair edge : graph.edges()) { copy.putEdge(edge.nodeU(), edge.nodeV()); } return copy; } /** Creates a mutable copy of {@code graph} with the same nodes, edges, and edge values. */ public static MutableValueGraph copyOf(ValueGraph graph) { MutableValueGraph copy = ValueGraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); for (N node : graph.nodes()) { copy.addNode(node); } for (EndpointPair edge : graph.edges()) { copy.putEdgeValue( edge.nodeU(), edge.nodeV(), graph.edgeValueOrDefault(edge.nodeU(), edge.nodeV(), null)); } return copy; } /** Creates a mutable copy of {@code network} with the same nodes and edges. */ public static MutableNetwork copyOf(Network network) { MutableNetwork copy = NetworkBuilder.from(network) .expectedNodeCount(network.nodes().size()) .expectedEdgeCount(network.edges().size()) .build(); for (N node : network.nodes()) { copy.addNode(node); } for (E edge : network.edges()) { EndpointPair endpointPair = network.incidentNodes(edge); copy.addEdge(endpointPair.nodeU(), endpointPair.nodeV(), edge); } return copy; } @CanIgnoreReturnValue static int checkNonNegative(int value) { checkArgument(value >= 0, "Not true that %s is non-negative.", value); return value; } @CanIgnoreReturnValue static long checkNonNegative(long value) { checkArgument(value >= 0, "Not true that %s is non-negative.", value); return value; } @CanIgnoreReturnValue static int checkPositive(int value) { checkArgument(value > 0, "Not true that %s is positive.", value); return value; } @CanIgnoreReturnValue static long checkPositive(long value) { checkArgument(value > 0, "Not true that %s is positive.", value); return value; } /** * An enum representing the state of a node during DFS. {@code PENDING} means that the node is on * the stack of the DFS, while {@code COMPLETE} means that the node and all its successors have * been already explored. Any node that has not been explored will not have a state at all. */ private enum NodeVisitState { PENDING, COMPLETE } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy