org.jgrapht.Graphs Maven / Gradle / Ivy
/*
* (C) Copyright 2003-2023, by Barak Naveh and Contributors.
*
* JGraphT : a free Java graph-theory library
*
* See the CONTRIBUTORS.md file distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the
* GNU Lesser General Public License v2.1 or later
* which is available at
* http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
*
* SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
*/
package org.jgrapht;
import org.jgrapht.graph.*;
import org.jgrapht.util.*;
import java.util.*;
import java.util.function.*;
/**
* A collection of utilities to assist with graph manipulation.
*
* @author Barak Naveh
*/
public abstract class Graphs
{
/**
* Creates a new edge and adds it to the specified graph similarly to the
* {@link Graph#addEdge(Object, Object)} method.
*
* @param g the graph for which the edge to be added
* @param sourceVertex source vertex of the edge
* @param targetVertex target vertex of the edge
* @param weight weight of the edge
* @param the graph vertex type
* @param the graph edge type
*
* @return The newly created edge if added to the graph, otherwise
* null
.
*
* @throws UnsupportedOperationException if the graph has no edge supplier
*
* @see Graph#addEdge(Object, Object)
*/
public static E addEdge(Graph g, V sourceVertex, V targetVertex, double weight)
{
Supplier edgeSupplier = g.getEdgeSupplier();
if (edgeSupplier == null) {
throw new UnsupportedOperationException("Graph contains no edge supplier");
}
E e = edgeSupplier.get();
if (g.addEdge(sourceVertex, targetVertex, e)) {
g.setEdgeWeight(e, weight);
return e;
} else {
return null;
}
}
/**
* Adds the specified source and target vertices to the graph, if not already included, and
* creates a new edge and adds it to the specified graph similarly to the
* {@link Graph#addEdge(Object, Object)} method.
*
* @param g the graph for which the specified edge to be added
* @param sourceVertex source vertex of the edge
* @param targetVertex target vertex of the edge
* @param the graph vertex type
* @param the graph edge type
*
* @return The newly created edge if added to the graph, otherwise
* null
.
*/
public static E addEdgeWithVertices(Graph g, V sourceVertex, V targetVertex)
{
g.addVertex(sourceVertex);
g.addVertex(targetVertex);
return g.addEdge(sourceVertex, targetVertex);
}
/**
* Adds the specified edge to the graph, including its vertices if not already included.
*
* @param targetGraph the graph for which the specified edge to be added
* @param sourceGraph the graph in which the specified edge is already present
* @param edge edge to add
* @param the graph vertex type
* @param the graph edge type
*
* @return true
if the target graph did not already contain the specified edge.
*/
public static boolean addEdgeWithVertices(Graph targetGraph, Graph sourceGraph, E edge)
{
V sourceVertex = sourceGraph.getEdgeSource(edge);
V targetVertex = sourceGraph.getEdgeTarget(edge);
targetGraph.addVertex(sourceVertex);
targetGraph.addVertex(targetVertex);
return targetGraph.addEdge(sourceVertex, targetVertex, edge);
}
/**
* Adds the specified source and target vertices to the graph, if not already included, and
* creates a new weighted edge and adds it to the specified graph similarly to the
* {@link Graph#addEdge(Object, Object)} method.
*
* @param g the graph for which the specified edge to be added
* @param sourceVertex source vertex of the edge
* @param targetVertex target vertex of the edge
* @param weight weight of the edge
* @param the graph vertex type
* @param the graph edge type
*
* @return The newly created edge if added to the graph, otherwise
* null
.
*/
public static E addEdgeWithVertices(Graph g, V sourceVertex, V targetVertex, double weight)
{
g.addVertex(sourceVertex);
g.addVertex(targetVertex);
return addEdge(g, sourceVertex, targetVertex, weight);
}
/**
* Adds all the vertices and all the edges of the specified source graph to the specified
* destination graph. First all vertices of the source graph are added to the destination graph.
* Then every edge of the source graph is added to the destination graph. This method returns
* true
if the destination graph has been modified as a result of this operation,
* otherwise it returns false
.
*
*
* The behavior of this operation is undefined if any of the specified graphs is modified while
* operation is in progress.
*
*
* @param destination the graph to which vertices and edges are added
* @param source the graph used as source for vertices and edges to add
* @param the graph vertex type
* @param the graph edge type
*
* @return true
if and only if the destination graph has been changed as a result
* of this operation.
*/
public static boolean addGraph(Graph destination, Graph source)
{
boolean modified = addAllVertices(destination, source.vertexSet());
modified |= addAllEdges(destination, source, source.edgeSet());
return modified;
}
/**
* Adds all the vertices and all the edges of the specified source digraph to the specified
* destination digraph, reversing all of the edges. If you want to do this as a linked view of
* the source graph (rather than by copying to a destination graph), use
* {@link EdgeReversedGraph} instead.
*
*
* The behavior of this operation is undefined if any of the specified graphs is modified while
* operation is in progress.
*
* @param destination the graph to which vertices and edges are added
* @param source the graph used as source for vertices and edges to add
* @param the graph vertex type
* @param the graph edge type
*
* @see EdgeReversedGraph
*/
public static void addGraphReversed(Graph destination, Graph source)
{
if (!source.getType().isDirected() || !destination.getType().isDirected()) {
throw new IllegalArgumentException("graph must be directed");
}
addAllVertices(destination, source.vertexSet());
for (E edge : source.edgeSet()) {
destination.addEdge(source.getEdgeTarget(edge), source.getEdgeSource(edge));
}
}
/**
* Adds a subset of the edges of the specified source graph to the specified destination graph.
* The behavior of this operation is undefined if either of the graphs is modified while the
* operation is in progress. {@link #addEdgeWithVertices} is used for the transfer, so source
* vertexes will be added automatically to the target graph.
*
* @param destination the graph to which edges are to be added
* @param source the graph used as a source for edges to add
* @param edges the edges to be added
* @param the graph vertex type
* @param the graph edge type
*
* @return true
if this graph changed as a result of the call
*/
public static boolean addAllEdges(
Graph destination, Graph source, Collection edges)
{
boolean modified = false;
for (E e : edges) {
V s = source.getEdgeSource(e);
V t = source.getEdgeTarget(e);
destination.addVertex(s);
destination.addVertex(t);
modified |= destination.addEdge(s, t, e);
}
return modified;
}
/**
* Adds all of the specified vertices to the destination graph. The behavior of this operation
* is undefined if the specified vertex collection is modified while the operation is in
* progress. This method will invoke the {@link Graph#addVertex(Object)} method.
*
* @param destination the graph to which edges are to be added
* @param vertices the vertices to be added to the graph
* @param the graph vertex type
* @param the graph edge type
*
* @return true
if graph changed as a result of the call
*
* @throws NullPointerException if the specified vertices contains one or more null vertices, or
* if the specified vertex collection is
* null
.
*
* @see Graph#addVertex(Object)
*/
public static boolean addAllVertices(
Graph destination, Collection vertices)
{
boolean modified = false;
for (V v : vertices) {
modified |= destination.addVertex(v);
}
return modified;
}
/**
* Returns a list of vertices that are the neighbors of a specified vertex. If the graph is a
* multigraph vertices may appear more than once in the returned list.
*
*
* The method uses {@link Graph#edgesOf(Object)} to traverse the graph.
*
* @param g the graph to look for neighbors in
* @param vertex the vertex to get the neighbors of
* @param the graph vertex type
* @param the graph edge type
*
* @return a list of the vertices that are the neighbors of the specified vertex.
*/
public static List neighborListOf(Graph g, V vertex)
{
List neighbors = new ArrayList<>();
for (E e : g.iterables().edgesOf(vertex)) {
neighbors.add(getOppositeVertex(g, e, vertex));
}
return neighbors;
}
/**
* Returns a set of vertices that are neighbors of a specified vertex.
*
* @param g the graph to look for neighbors in
* @param vertex the vertex to get the neighbors of
* @param the graph vertex type
* @param the graph edge type
* @return a set of the vertices that are neighbors of the specified vertex
*/
public static Set neighborSetOf(Graph g, V vertex)
{
Set neighbors = new LinkedHashSet<>();
for (E e : g.iterables().edgesOf(vertex)) {
neighbors.add(Graphs.getOppositeVertex(g, e, vertex));
}
return neighbors;
}
/**
* Returns a list of vertices that are the direct predecessors of a specified vertex. If the
* graph is a multigraph, vertices may appear more than once in the returned list.
*
*
* The method uses {@link Graph#incomingEdgesOf(Object)} to traverse the graph.
*
* @param g the graph to look for predecessors in
* @param vertex the vertex to get the predecessors of
* @param the graph vertex type
* @param the graph edge type
*
* @return a list of the vertices that are the direct predecessors of the specified vertex.
*/
public static List predecessorListOf(Graph g, V vertex)
{
List predecessors = new ArrayList<>();
for (E e : g.iterables().incomingEdgesOf(vertex)) {
predecessors.add(getOppositeVertex(g, e, vertex));
}
return predecessors;
}
/**
* Returns a list of vertices that are the direct successors of a specified vertex. If the graph
* is a multigraph vertices may appear more than once in the returned list.
*
*
* The method uses {@link Graph#outgoingEdgesOf(Object)} to traverse the graph.
*
* @param g the graph to look for successors in
* @param vertex the vertex to get the successors of
* @param the graph vertex type
* @param the graph edge type
*
* @return a list of the vertices that are the direct successors of the specified vertex.
*/
public static List successorListOf(Graph g, V vertex)
{
List successors = new ArrayList<>();
for (E e : g.iterables().outgoingEdgesOf(vertex)) {
successors.add(getOppositeVertex(g, e, vertex));
}
return successors;
}
/**
* Returns an undirected view of the specified graph. If the specified graph is directed,
* returns an undirected view of it. If the specified graph is already undirected, just returns
* it.
*
* @param g the graph for which an undirected view is to be returned
* @param the graph vertex type
* @param the graph edge type
*
* @return an undirected view of the specified graph, if it is directed, or or the specified
* graph itself if it is already undirected.
*
* @throws IllegalArgumentException if the graph is neither directed nor undirected
* @see AsUndirectedGraph
*/
public static Graph undirectedGraph(Graph g)
{
if (g.getType().isDirected()) {
return new AsUndirectedGraph<>(g);
} else if (g.getType().isUndirected()) {
return g;
} else {
throw new IllegalArgumentException("graph must be either directed or undirected");
}
}
/**
* Tests whether an edge is incident to a vertex.
*
* @param g graph containing e and v
* @param e edge in g
* @param v vertex in g
* @param the graph vertex type
* @param the graph edge type
*
* @return true iff e is incident on v
*/
public static boolean testIncidence(Graph g, E e, V v)
{
return (g.getEdgeSource(e).equals(v)) || (g.getEdgeTarget(e).equals(v));
}
/**
* Gets the vertex opposite another vertex across an edge.
*
* @param g graph containing e and v
* @param e edge in g
* @param v vertex in g
* @param the graph vertex type
* @param the graph edge type
*
* @return vertex opposite to v across e
*/
public static V getOppositeVertex(Graph g, E e, V v)
{
V source = g.getEdgeSource(e);
V target = g.getEdgeTarget(e);
if (v.equals(source)) {
return target;
} else if (v.equals(target)) {
return source;
} else {
throw new IllegalArgumentException("no such vertex: " + v.toString());
}
}
/**
* Removes the given vertex from the given graph. If the vertex to be removed has one or more
* predecessors, the predecessors will be connected directly to the successors of the vertex to
* be removed.
*
* @param graph graph to be mutated
* @param vertex vertex to be removed from this graph, if present
* @param the graph vertex type
* @param the graph edge type
*
* @return true if the graph contained the specified vertex; false otherwise.
*/
public static boolean removeVertexAndPreserveConnectivity(Graph graph, V vertex)
{
if (!graph.containsVertex(vertex)) {
return false;
}
if (vertexHasPredecessors(graph, vertex)) {
List predecessors = Graphs.predecessorListOf(graph, vertex);
List successors = Graphs.successorListOf(graph, vertex);
for (V predecessor : predecessors) {
addOutgoingEdges(graph, predecessor, successors);
}
}
graph.removeVertex(vertex);
return true;
}
/**
* Filters vertices from the given graph and subsequently removes them. If the vertex to be
* removed has one or more predecessors, the predecessors will be connected directly to the
* successors of the vertex to be removed.
*
* @param graph graph to be mutated
* @param predicate a non-interfering stateless predicate to apply to each vertex to determine
* if it should be removed from the graph
* @param the graph vertex type
* @param the graph edge type
*
* @return true if at least one vertex has been removed; false otherwise.
*/
public static boolean removeVerticesAndPreserveConnectivity(Graph graph, Predicate predicate)
{
List verticesToRemove = new ArrayList<>();
for (V node : graph.vertexSet()) {
if (predicate.test(node)) {
verticesToRemove.add(node);
}
}
return removeVertexAndPreserveConnectivity(graph, verticesToRemove);
}
/**
* Removes all the given vertices from the given graph. If the vertex to be removed has one or
* more predecessors, the predecessors will be connected directly to the successors of the
* vertex to be removed.
*
* @param graph to be mutated
* @param vertices vertices to be removed from this graph, if present
* @param the graph vertex type
* @param the graph edge type
*
* @return true if at least one vertex has been removed; false otherwise.
*/
public static boolean removeVertexAndPreserveConnectivity(Graph graph, Iterable vertices)
{
boolean atLeastOneVertexHasBeenRemoved = false;
for (V vertex : vertices) {
if (removeVertexAndPreserveConnectivity(graph, vertex)) {
atLeastOneVertexHasBeenRemoved = true;
}
}
return atLeastOneVertexHasBeenRemoved;
}
/**
* Add edges from one source vertex to multiple target vertices. Whether duplicates are created
* depends on the underlying {@link Graph} implementation.
*
* @param graph graph to be mutated
* @param source source vertex of the new edges
* @param targets target vertices for the new edges
* @param the graph vertex type
* @param the graph edge type
*/
public static void addOutgoingEdges(Graph graph, V source, Iterable targets)
{
if (!graph.containsVertex(source)) {
graph.addVertex(source);
}
for (V target : targets) {
if (!graph.containsVertex(target)) {
graph.addVertex(target);
}
graph.addEdge(source, target);
}
}
/**
* Add edges from multiple source vertices to one target vertex. Whether duplicates are created
* depends on the underlying {@link Graph} implementation.
*
* @param graph graph to be mutated
* @param target target vertex for the new edges
* @param sources source vertices for the new edges
* @param the graph vertex type
* @param the graph edge type
*/
public static void addIncomingEdges(Graph graph, V target, Iterable sources)
{
if (!graph.containsVertex(target)) {
graph.addVertex(target);
}
for (V source : sources) {
if (!graph.containsVertex(source)) {
graph.addVertex(source);
}
graph.addEdge(source, target);
}
}
/**
* Check if a vertex has any direct successors.
*
* @param graph the graph to look for successors
* @param vertex the vertex to look for successors
* @param the graph vertex type
* @param the graph edge type
*
* @return true if the vertex has any successors, false otherwise
*/
public static boolean vertexHasSuccessors(Graph graph, V vertex)
{
return !graph.outgoingEdgesOf(vertex).isEmpty();
}
/**
* Check if a vertex has any direct predecessors.
*
* @param graph the graph to look for predecessors
* @param vertex the vertex to look for predecessors
* @param the graph vertex type
* @param the graph edge type
*
* @return true if the vertex has any predecessors, false otherwise
*/
public static boolean vertexHasPredecessors(Graph graph, V vertex)
{
return !graph.incomingEdgesOf(vertex).isEmpty();
}
/**
* Compute a new mapping from the vertices of a graph to the integer range $[0, n)$ where $n$ is
* the number of vertices in the graph.
*
* @param graph the input graph
* @param the graph vertex type
* @param the graph edge type
* @throws NullPointerException if {@code graph} is {@code null}
*
* @return the mapping as an object containing the {@code vertexMap} and the {@code indexList}
*
* @see VertexToIntegerMapping
*/
public static VertexToIntegerMapping getVertexToIntegerMapping(Graph graph)
{
return new VertexToIntegerMapping<>(Objects.requireNonNull(graph).vertexSet());
}
}