/** * 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. * * <p>This method will detect any non-empty cycle, including self-loops (a cycle of length 1). */ public static <N> boolean hasCycle(Graph<N> 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<Object, NodeVisitState> visitedNodes = Maps.newHashMapWithExpectedSize(graph.nodes().size()); for (N node : graph.nodes()) { if (subgraphHasCycle(graph, visitedNodes, node, null)) { return true; } } return false; }
@Override public final boolean equals(@Nullable Object obj) { if (obj == this) { return true; } if (!(obj instanceof Graph)) { return false; } Graph<?> other = (Graph<?>) obj; return isDirected() == other.isDirected() && nodes().equals(other.nodes()) && edges().equals(other.edges()); }
/** Returns an immutable copy of {@code graph}. */ public static <N> ImmutableGraph<N> copyOf(Graph<N> graph) { return (graph instanceof ImmutableGraph) ? (ImmutableGraph<N>) graph : new ImmutableGraph<N>( new ConfigurableValueGraph<N, Presence>( GraphBuilder.from(graph), getNodeConnections(graph), graph.edges().size())); }
/** * 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. * * <p>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()); }
/** * 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. * * <p>This method will detect any non-empty cycle, including self-loops (a cycle of length 1). */ public static <N> boolean hasCycle(Graph<N> 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<Object, NodeVisitState> visitedNodes = Maps.newHashMapWithExpectedSize(graph.nodes().size()); for (N node : graph.nodes()) { if (subgraphHasCycle(graph, visitedNodes, node, null)) { return true; } } return false; }
@Override public final boolean equals(@NullableDecl Object obj) { if (obj == this) { return true; } if (!(obj instanceof Graph)) { return false; } Graph<?> other = (Graph<?>) obj; return isDirected() == other.isDirected() && nodes().equals(other.nodes()) && edges().equals(other.edges()); }
/** Returns an immutable copy of {@code graph}. */ public static <N> ImmutableGraph<N> copyOf(Graph<N> graph) { return (graph instanceof ImmutableGraph) ? (ImmutableGraph<N>) graph : new ImmutableGraph<N>( new ConfigurableValueGraph<N, Presence>( GraphBuilder.from(graph), getNodeConnections(graph), graph.edges().size())); }
/** Creates a mutable copy of {@code graph} with the same nodes and edges. */ public static <N> MutableGraph<N> copyOf(Graph<N> graph) { MutableGraph<N> copy = GraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); for (N node : graph.nodes()) { copy.addNode(node); } for (EndpointPair<N> edge : graph.edges()) { copy.putEdge(edge.nodeU(), edge.nodeV()); } return copy; }
/** Returns an immutable copy of {@code graph}. */ public static <N> ImmutableGraph<N> copyOf(Graph<N> graph) { return (graph instanceof ImmutableGraph) ? (ImmutableGraph<N>) graph : new ImmutableGraph<N>( new ConfigurableValueGraph<N, Presence>( GraphBuilder.from(graph), getNodeConnections(graph), graph.edges().size())); }
/** * 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. * * <p>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()); }
@Override public final boolean equals(@NullableDecl Object obj) { if (obj == this) { return true; } if (!(obj instanceof Graph)) { return false; } Graph<?> other = (Graph<?>) obj; return isDirected() == other.isDirected() && nodes().equals(other.nodes()) && edges().equals(other.edges()); }
@Test public void edges_containsOrderMismatch() { addEdge(N1, N2, E12); EndpointPair<Integer> endpointsN1N2 = EndpointPair.unordered(N1, N2); EndpointPair<Integer> endpointsN2N1 = EndpointPair.unordered(N2, N1); assertThat(network.asGraph().edges()).doesNotContain(endpointsN1N2); assertThat(network.asGraph().edges()).doesNotContain(endpointsN2N1); }
/** * Merge all nodes and edges of two graphs. * * @param graph1 A {@link MutableGraph} into which all nodes and edges of {@literal graph2} will be merged * @param graph2 The {@link Graph} whose nodes and edges will be merged into {@literal graph1} * @param <N> The class of the nodes */ public static <N> void merge(MutableGraph<N> graph1, Graph<N> graph2) { for (N node : graph2.nodes()) { graph1.addNode(node); } for (EndpointPair<N> edge : graph2.edges()) { graph1.putEdge(edge.nodeU(), edge.nodeV()); } } }
/** Creates a mutable copy of {@code graph} with the same nodes and edges. */ public static <N> MutableGraph<N> copyOf(Graph<N> graph) { MutableGraph<N> copy = GraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); for (N node : graph.nodes()) { copy.addNode(node); } for (EndpointPair<N> 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 <N, V> MutableValueGraph<N, V> copyOf(ValueGraph<N, V> graph) { MutableValueGraph<N, V> copy = ValueGraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build(); for (N node : graph.nodes()) { copy.addNode(node); } for (EndpointPair<N> edge : graph.edges()) { copy.putEdgeValue( edge.nodeU(), edge.nodeV(), graph.edgeValueOrDefault(edge.nodeU(), edge.nodeV(), null)); } return copy; }
@After public void validateGraphState() { assertStronglyEquivalent(graph, Graphs.copyOf(graph)); assertStronglyEquivalent(graph, ImmutableValueGraph.copyOf(graph)); Graph<Integer> asGraph = graph.asGraph(); AbstractGraphTest.validateGraph(asGraph); assertThat(graph.nodes()).isEqualTo(asGraph.nodes()); assertThat(graph.edges()).isEqualTo(asGraph.edges()); assertThat(graph.nodeOrder()).isEqualTo(asGraph.nodeOrder()); assertThat(graph.isDirected()).isEqualTo(asGraph.isDirected()); assertThat(graph.allowsSelfLoops()).isEqualTo(asGraph.allowsSelfLoops()); for (Integer node : graph.nodes()) { assertThat(graph.adjacentNodes(node)).isEqualTo(asGraph.adjacentNodes(node)); assertThat(graph.predecessors(node)).isEqualTo(asGraph.predecessors(node)); assertThat(graph.successors(node)).isEqualTo(asGraph.successors(node)); assertThat(graph.degree(node)).isEqualTo(asGraph.degree(node)); assertThat(graph.inDegree(node)).isEqualTo(asGraph.inDegree(node)); assertThat(graph.outDegree(node)).isEqualTo(asGraph.outDegree(node)); for (Integer otherNode : graph.nodes()) { boolean hasEdge = graph.hasEdgeConnecting(node, otherNode); assertThat(hasEdge).isEqualTo(asGraph.hasEdgeConnecting(node, otherNode)); assertThat(graph.edgeValueOrDefault(node, otherNode, null) != null).isEqualTo(hasEdge); assertThat(!graph.edgeValueOrDefault(node, otherNode, DEFAULT).equals(DEFAULT)) .isEqualTo(hasEdge); } } }
@Test public void endpointPair_directedNetwork() { MutableNetwork<Integer, String> directedNetwork = NetworkBuilder.directed().allowsSelfLoops(true).build(); directedNetwork.addNode(N0); directedNetwork.addEdge(N1, N2, E12); directedNetwork.addEdge(N2, N1, E21); directedNetwork.addEdge(N1, N3, E13); directedNetwork.addEdge(N4, N4, E44); containsExactlySanityCheck( directedNetwork.asGraph().edges(), EndpointPair.ordered(N1, N2), EndpointPair.ordered(N2, N1), EndpointPair.ordered(N1, N3), EndpointPair.ordered(N4, N4)); }
@Test public void endpointPair_undirectedNetwork() { MutableNetwork<Integer, String> undirectedNetwork = NetworkBuilder.undirected().allowsParallelEdges(true).allowsSelfLoops(true).build(); undirectedNetwork.addNode(N0); undirectedNetwork.addEdge(N1, N2, E12); undirectedNetwork.addEdge(N2, N1, E12_A); // adds parallel edge, won't be in Graph edges undirectedNetwork.addEdge(N1, N3, E13); undirectedNetwork.addEdge(N4, N4, E44); containsExactlySanityCheck( undirectedNetwork.asGraph().edges(), EndpointPair.unordered(N1, N2), EndpointPair.unordered(N1, N3), EndpointPair.unordered(N4, N4)); }