0

One thought is run a spanning tree algorithm (e.g., Kruskal) to get the spanning tree, and then construct a new graph from the spanning tree, which is quite messy in terms of code. Looks like there is no available implementation to obtain a DAG subgraph of a directed graph. Please instruct if there are better ways of doing this with jgrapht. Thanks!

As described in the previous section.

Liang Lu
  • 3
  • 1
  • Hi there, welcome to stackoverflow! Did any of the answers answer your question? If so, click the 'accept' button and upvote; if not, consider updating/clarifying your question. – Joris Kinable Feb 25 '23 at 06:30

2 Answers2

0

It rather depends on why you want to do this and what changes to your graph you will accept.

The straightforward way would be to find the cycles in the graph and select one of the edges in each cycle to be removed. However, deciding which edge to remove to remove from each cycle is a problem. You do not provide enough information in your question to make any suggestions about this.

To find the cycles in a graph only requires a modification to a simple depth first search that checks for a 'back edge' whenever a previously visited vertex is reached again. Here is some C++ code to do this:

https://github.com/JamesBremner/PathFinder/blob/d557d433e6676a1c066bc9ff5f5fdd8c8e3d9c61/src/GraphTheory.cpp#L247-L297

Here is a screenshot of a test run

enter image description here

Here are the results of a timing test on a significantly sized graph:

4,720 vertices 27,444 edges 13,722 cycles => 700 seconds

Of course, removing edges to break all these cycles will leave the graph in a horrible mess. Which is why I wonder why you want to do this. I asked you why, but you still haven't answered!

ravenspoint
  • 19,093
  • 6
  • 57
  • 103
  • I didn't want to go with the route of detecting cycles and the removing edges, because this seems like not a efficient method for very large graphs with lots of cycles. Thanks for the answer! – Liang Lu Feb 26 '23 at 03:56
  • You will have a very hard time finding an algorithm that explores a graph any faster than depth first search. You do not say how large your graph is. Nor do you say how many cycles are in your graph. So, anyway, I ran a timing test om a fairly significantly sized graph. Result added to my answer. – ravenspoint Feb 26 '23 at 15:10
  • My application is in supply chain (in terms of physical sites and their connections), so the graph is not large. I was mainly concerned about the cleanness of code and ease of maintenance (followed by code efficiency), so the best option for me is to leverage an existing implementation (ideally provided by jgrapht itself) for this task. Thanks for looking into this for me! – Liang Lu Feb 27 '23 at 16:41
  • In your first comment "a efficient method for very large graphs with lots of cycles" In your second you say " the graph is not large." So which is it? – ravenspoint Feb 27 '23 at 16:50
0

Your suggestion is not necessary bad. Not sure why this would be messy in code:

Graph<Integer,DefaultEdge> graphWithCycles = ...; //Your graph with cycles

//Compute spanning tree
SpanningTreeAlgorithm<DefaultEdge> sa = new KruskalMinimumSpanningTree<>(graphWithCycles);
SpanningTreeAlgorithm.SpanningTree<DefaultEdge> tree = sa.getSpanningTree();

//Create new graph without cycles induced by the spanning tree
Graph<Integer,DefaultEdge> graphWithoutCycles = ...; //Your graph without cycles, definition depends on graphWithCycles
for(DefaultEdge edge: tree.getEdges())
    Graphs.addEdgeWithVertices​(graphWithoutCycles, graphWithCycles, edge);

There's various other approaches, e.g. you could also simply run a BreadthFirstSearch on your graph with cycles and create a new graph induced by the BFS tree as you go:

Graph<Integer,DefaultEdge> graphWithCycles = ...; //Your graph with cycles
Graph<Integer,DefaultEdge> graphWithoutCycles = ...; //Your graph without cycles
BreadthFirstIterator​ bfs = new BreadthFirstIterator​(graphWithCycles);
while(bfs.hasNext()){
    Integer vertex = bfs.next(); 
    graphWithoutCycles.addVertex(vertex);
    DefaultEdge edge = bfs.getSpanningTreeEdge(vertex);
    if(edge != null) //Edge is null if the vertex is the root of the BFS tree
        graphWithoutCycles.addEdge(bfs.getParent(vertex),vertex,edge);
}
Joris Kinable
  • 2,232
  • 18
  • 29
  • Appreciate the cleanness of the solution! – Liang Lu Feb 26 '23 at 03:57
  • Kruskal will only find a spanning tree if the graph is connected. So this algorithm is liable to leave some of the vertices out of the new graph, as well as removing many more edges from the graph than is necessary. – ravenspoint Feb 27 '23 at 15:01
  • Could identify the sub-graphs first and then run Kruskal on each sub-graph. – Liang Lu Feb 27 '23 at 16:37
  • @ravenspoint your observation is correct - you would have to run Kruskal for every connected component. To obtain a list of connected components in JGrapht, use `ConnectivityInspector`. As a better alternative, I would recommend using the BFS approach as it will automatically traverse across all connected components whenever no explicit starting vertex (root) is specified, see https://jgrapht.org/javadoc/org.jgrapht.core/org/jgrapht/traverse/BreadthFirstIterator.html – Joris Kinable Feb 27 '23 at 19:21