How to efficiently generate all possible spanning trees from a graph
Asked Answered
C

3

12

First please note that this question is NOT asking about MST, instead, just all possible spanning trees.

So this is NOT the same as finding all minimal spanning trees or All minimum spanning trees implementation


I just need to generate all possible spanning trees from a graph.

I think the brute-force way is straight:

Suppose we have V nodes and E edges.

  1. Get all edges of the graph
  2. Get all possible combinations of V-1 out of E edges.
  3. Filter out non-spanning-tree out of the combinations (for a spanning tree, all nodes inside one set of V-1 edges should appear exactly once)

But I think it is too slow when facing big graph.

Do we have a better way?

Commutate answered 2/3, 2014 at 13:54 Comment(2)
Actually the algorithm you link to will work for you after you set all edge weights to the same value. Most obvious choice for weights would be 1 or 0, but it's entirely irrelevant (apart from overflow issues if there are any).Mosora
@G.Bach could you please transform your comment to an answer?Commutate
M
11

Set the weight of all edges to the same value, then use an algorithm to find all minimum spanning trees. Since all spanning trees have |V|-1 edges and all edge weights are equal, all spanning trees will be minimum spanning trees.

Mosora answered 2/3, 2014 at 15:21 Comment(7)
I suppose that this is a correct answer, but it recalls to me the joke about a mathematician boiling water because every MST enumeration algorithm that I know of enumerates all spanning trees in the subgraph of safe edges.Tetzel
@DavidEisenstat I've never had a look at any algorithm enumerating MSTs, so only having that as a black box, the obvious approach is my answer. I do like that joke, though. Not sure whether it's making fun of engineers or mathematicians, or both. EDIT: thanks for that website, haven't laughed like that in a while.Mosora
@DavidEisenstat do you have any better idea?Commutate
@JacksonTale I don't know what algorithms to enumerate MSTs are out there, but considering that all the ones David knows enumerate all spanning trees of some subset, you might want to have a look at those in detail and just throw out the restrictions limiting the algorithm to the safe edges.Mosora
@JacksonTale I somehow got it into my head that David Eppstein's 1995 paper (citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.39.8848) was simpler than it actually is. There's an algorithm for generating spanning trees in Knuth's Volume 4 of TAoCP.Tetzel
A BFS or DFS also would allow enumeration of spanning trees no need for MST algorithmsVertex
-1 because this answer basically says, "to enumerate all spanning trees, simply use an algorithm that enumerates all spanning trees". That is not useful. Generating each spanning tree precisely once (i.e. making sure that you get all of them without creating duplicates) is not exactly trivial.Stockholm
O
3

I've become interested in this question, and have yet to find a really satisfactory answer. However, I have found a number of references: Knuth's Algorithms S and S' in TAOCP Volume 4 Fascicle 4, a paper by Sorensen and Janssens, and GRAYSPAN, SPSPAN, and GRAYSPSPAN by Knuth. It's too bad none of them are implementations in a language I could use ... I guess I'll have to spend some time coding these ...

Ostentation answered 25/8, 2015 at 4:14 Comment(0)
S
0

You can use the contraction-deletion algorithm to generate all spanning trees of undirected graph. There is a fast python code in github called Spanning Trees Generator:

from itertools import product
import networkx as nx 
import numpy as np 
import matplotlib.pyplot as plt

#Optional: Calculate the total number of spanning trees for a given graph (g) using the Matrix Tree Theorem (Kirchhoff's theorem),
def numSpanTrees(g)-> int:
    if not nx.is_connected(g):
        print("Only for connected graphs")
        return 0
    if nx.is_directed(g): nx.to_undirected(g)
    n = g.number_of_nodes()
    laplacian_matrix = nx.laplacian_matrix(g).toarray() #Calculate the Laplacian matrix
    cofactor_matrix = laplacian_matrix[1:n, 1:n]  # Choose any cofactor by excluding the first row and column
    determinant = np.linalg.det(cofactor_matrix)  #Calculate the determinant of an cofactor of Laplacian matrix
    return int(np.round(abs(determinant)))
#Generate all spanning trees using contraction-deletion algorithm:
def spanTrees(trs, Edg, all_span_trees, k):
    if k == 0:
        all_span_trees.extend(product(*trs))#Expand parallels edges using the Cartesian product
    
    for i in range(k):
        if Edg[k][i] == []: continue
        trs.append(Edg[k][i])
        Edg[i] = [Edg[i][j] + Edg[k][j] for j in range(i)]#Contraction 
        spanTrees(trs, Edg, all_span_trees, k-1)
        trs.pop()
        [Edg[i][j].pop() for j in range(i) for _ in range(len(Edg[k][j]))]#Deletion
#Helper function
def Spanning_Trees_Generator(g, hch = False):
    if not nx.is_connected(g):
        print("Only for connected graphs")
        return 0
    if nx.is_directed(g): nx.to_undirected(g)
    n = g.number_of_nodes()
    edgs = list(g.edges)
    Edg = [[[] for _ in range(n)] for _ in range(n)]
    mx = len(edgs)
    edgNum = dict() #dictionary designed to store labeled edges where the keys are integer labels for the edges, and the values are tuples representing the ordinary edge definitions (in, out)

    for ed in edgs:
        i, j = sorted(ed)
        Edg[j][i] = [mx]
        edgNum[mx] = ed
        mx -= 1
    all_span_trees = []
    spanTrees([], Edg, all_span_trees, n-1)
  
    if hch:  
        return all_span_trees #Generate only the labeled edges (Keys) 
    else:
        return [nx.Graph(edgNum[k] for k in element) for element in all_span_trees] #Generate spanning trees as NetworkX graphics objects

# Example usage
g = nx.complete_graph(5)
print("Number of Spanning Trees : ", numSpanTrees(g)) #Optional and only for undirected graph
spanTreesGraph = Spanning_Trees_Generator(g)
        
#Plot the first spanning tree
pos = nx.circular_layout(g)
nx.draw(g, pos, with_labels=True, node_color='lightblue', node_size=200, edge_color='b')
nx.draw(spanTreesGraph[0], pos, with_labels=True, node_color='lightgreen', node_size=200, edge_color='r')
plt.show()

https://github.com/tagtog12000/SpanTree/blob/master/Spanning%20Trees%20Generator.py

Sharper answered 15/10, 2023 at 12:36 Comment(2)
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From ReviewOriflamme
Okay, done. My code has been added to the answer.Sharper

© 2022 - 2024 — McMap. All rights reserved.