3

I have a toy graph g, then I have found the number of spanning trees by cofactor of the Laplacian. The number is 11.

library(igraph)
set.seed(2)
n <- 5   #  n=5
m <- 6   #  m=6
g <- erdos.renyi.game(n, p.or.m=m, type="gnm" , directed=FALSE, loops=FALSE)

lap_mat <- laplacian_matrix(g)   
print(paste("number of spanning trees by cofactor = ", det(lap_mat[-1,-1])))
# 11

I have n=5 verteces, and I plot the original graph:

par(mfrow=c(2, 3))
layout <- layout.fruchterman.reingold(g)
plot(g, main="Original graph", vertex.size = 40, layout = layout)

then 5 spanning trees were created with the graph.bfs() function:

for (i in 1:n) {
  r <- graph.bfs(g, root=i, order=TRUE, father=TRUE)
  h <- graph(rbind(r$order, r$father[r$order, na_ok = TRUE])[, -1], directed=FALSE)
  plot(h, main=paste("Spanning tree for root=", i), vertex.size = 40, layout = layout)
}

enter image description here

I need to plot all spanning trees. For example, the next tree with root = 5 is not created:

enter image description here

Question. What is a possible way to generate all trees for small random graph?

Sandipan Dey
  • 21,482
  • 2
  • 51
  • 63
Nick
  • 1,086
  • 7
  • 21
  • Where do you get 11 trees? You loop from 1 to `n = 5` so end up with 5 trees. You have 5 vertices, the Laplacian has the dimension `5 x 5`, and there are 5 roots to start with for the breadth-first search. – Maurits Evers Nov 01 '21 at 10:21
  • @MauritsEvers, to find the total number of spanning trees in the given graph, I calculated the cofactor of (1,1)-element in the Laplacian matrix 'det(lap_mat[-1,-1])'. This number is equivalent to the total number of the spanning trees in the graph. – Nick Nov 01 '21 at 11:05
  • You loop through all vertices`for (i in 1:n)`. Since `n=5` (by your design), you'll end up with 5 graphs. Not sure how you expect this to produce 11 graphs. Perhaps this is an XY problem? – Maurits Evers Nov 01 '21 at 23:36
  • @MauritsEvers, yes, the n in (1,5), because loop for the vertecies. What are you mean XY problem? Is a problem in layout? – Nick Nov 02 '21 at 02:27
  • I think we're not understanding each other. You loop through all vertices, and since you have 5 vertices you produce 5 graphs. Not 11. This is by design of your toy graph and I'm not sure what you're trying to do. By [XY problem](https://en.wikipedia.org/wiki/XY_problem) I mean that the issue does not actually seem to be the plotting but something else. Perhaps you are asking how to calculate all spanning trees? – Maurits Evers Nov 02 '21 at 03:53
  • @MauritsEvers, thank you for reply. Yes, I need the calculate and plot all spanning tree in a loop (in my case 11 trees). I hope, my problem is not with loop, but is how to define parametrs for the graph.bfs() function. – Nick Nov 02 '21 at 04:02
  • @MauritsEvers The question is quite clear: Given an undirected graph, how do you generate all spanning trees? Nick is correct that there are 11 spanning trees according to [Kirchhoff's theorem](https://en.wikipedia.org/wiki/Kirchhoff%27s_theorem). However, a BFS cannot generate all spanning trees, no matter what node you start from. The code Nick shows does not generate all spanning trees, only all BFS trees, a small subset. – Szabolcs Nov 02 '21 at 17:39
  • igraph does not currently have any built-in algorithm to generate all spanning trees. It has an algorithm to sample spanning trees uniformly, but it is not yet exposed in the R interface. It might be exposed in version 1.3. Given the enormous number of spanning trees any non-tiny graph has, this may be a more useful approach. If you want all spanning trees, you will need to implement an algorithm yourself. Searching StackOverflow reveals that The Art Of Computer Programming Volume 4, Fascicle 4 has such an algorithm. You may also open a feature request for igraph. – Szabolcs Nov 02 '21 at 17:44
  • Sorry, correction: the code in the post doesn't even generate all BFS trees, just some of them. Still, not all spanning trees are BFS trees. – Szabolcs Nov 02 '21 at 17:53
  • 1
    @Szabolcs Yes the question is clearer now. originally it wasn't obvious that OP's confusion was around how to *calculate* all spanning trees (ST). A review on algorithms for generating STs is [Algorithms for generating all possible spanning trees of a simple undirected connected graph: an extensive review](https://link.springer.com/article/10.1007/s40747-018-0079-7). A lot of the methods use a BFS to generate an *initial* ST; all other STs can then be generated from this initial ST. I recommend taking a look at the review. It shouldn't be (too) difficult to implement one of these methods in R. – Maurits Evers Nov 02 '21 at 22:05
  • @MauritsEvers Excellent resource, thanks for posting! – Szabolcs Nov 02 '21 at 22:10
  • 1
    I opened a feature request: https://github.com/igraph/igraph/issues/1827 @Nick If you could comment on your motivation, it would help in prioritizing the issue. – Szabolcs Nov 02 '21 at 22:17
  • @Szabolcs, I have added my motivation on the request. – Nick Nov 03 '21 at 02:53

2 Answers2

2

First of all, I would say, my solution below is a brute-force method, thus only working well for graphs of small size, i.e., not many vertices or arcs.

If you have large networks, you should refer to some more advanced algorithms, e.g., https://link.springer.com/article/10.1007/s40747-018-0079-7


Since you have 6 arcs and 5 vertices, you only need to remove 2 arcs out of 6 to find the spanning tree. There would be combn(6,2) options, and you can delete those edge combinations one by one to check if a spanning tree remains

Filter(
  function(x) length(decompose(x)) == 1,
  combn(
    ecount(g),
    ecount(g) - vcount(g) + 1,
    FUN = function(x) delete.edges(g, E(g)[x]),
    simplify = FALSE
  )
)

which gives all 11 spanning trees

[[1]]
IGRAPH 9692f3d U--- 5 4 -- Erdos renyi (gnm) graph
+ attr: name (g/c), type (g/c), loops (g/l), m (g/n)
+ edges from 9692f3d:
[1] 2--4 3--4 1--5 2--5

[[2]]
IGRAPH 969368e U--- 5 4 -- Erdos renyi (gnm) graph
+ attr: name (g/c), type (g/c), loops (g/l), m (g/n)
+ edges from 969368e:
[1] 1--3 3--4 1--5 2--5

[[3]]
IGRAPH 969368e U--- 5 4 -- Erdos renyi (gnm) graph
+ attr: name (g/c), type (g/c), loops (g/l), m (g/n)
+ edges from 969368e:
[1] 1--3 2--4 1--5 2--5

[[4]]
IGRAPH 96938fa U--- 5 4 -- Erdos renyi (gnm) graph
+ attr: name (g/c), type (g/c), loops (g/l), m (g/n)
+ edges from 96938fa:
[1] 1--3 2--4 3--4 2--5

[[5]]
IGRAPH 96938fa U--- 5 4 -- Erdos renyi (gnm) graph
+ attr: name (g/c), type (g/c), loops (g/l), m (g/n)
+ edges from 96938fa:
[1] 1--3 2--4 3--4 1--5

[[6]]
IGRAPH 9693ded U--- 5 4 -- Erdos renyi (gnm) graph
+ attr: name (g/c), type (g/c), loops (g/l), m (g/n)
+ edges from 9693ded:
[1] 1--2 2--4 3--4 2--5

[[7]]
IGRAPH 969404b U--- 5 4 -- Erdos renyi (gnm) graph
+ attr: name (g/c), type (g/c), loops (g/l), m (g/n)
+ edges from 969404b:
[1] 1--2 2--4 3--4 1--5

[[8]]
IGRAPH 96942b7 U--- 5 4 -- Erdos renyi (gnm) graph
+ attr: name (g/c), type (g/c), loops (g/l), m (g/n)
+ edges from 96942b7:
[1] 1--2 1--3 3--4 2--5

[[9]]
IGRAPH 9694527 U--- 5 4 -- Erdos renyi (gnm) graph
+ attr: name (g/c), type (g/c), loops (g/l), m (g/n)
+ edges from 9694527:
[1] 1--2 1--3 3--4 1--5

[[10]]
IGRAPH 9694527 U--- 5 4 -- Erdos renyi (gnm) graph
+ attr: name (g/c), type (g/c), loops (g/l), m (g/n)
+ edges from 9694527:
[1] 1--2 1--3 2--4 2--5

[[11]]
IGRAPH 9694797 U--- 5 4 -- Erdos renyi (gnm) graph
+ attr: name (g/c), type (g/c), loops (g/l), m (g/n)
+ edges from 9694797:
[1] 1--2 1--3 2--4 1--5
ThomasIsCoding
  • 96,636
  • 9
  • 24
  • 81
1

First let's notice the following:

  1. For the original graph G, we have |V(G)| = 5, |E(G)| = 6

    enter image description here

  2. A spanning tree T of graph G will have exactly V(G)-1=4 vertices.

  3. But that does not mean that we can arbitrarily select and delete any 2 edges from G to obtain a spanning tree T. For example, if we choose to remove the edges (1,5) and (2,5), we shall obtain the following disconnected graph, which is not a tree:

    enter image description here

  4. Let's find the cycles in the graph starting from vertex 1. Note that since there is an edge from vertex 1 to 2, finding all possible paths (of length >1) starting from 1 and ending on vertex 2. Now, if we extend each of the paths by adding the edge (2,1) in the end, we shall find all possible cycles in the simple graph G, starting/ending on vertex 1, as it's done in the next code block:

    cycle_edges <- c()
    C <- list() # all possible cycles staring / ending on vertex 1
    for (path in all_simple_paths(g, 1, 2, mode="out")) {
       pn <- length(path)
       if (pn > 2) { # not a single edge
          cycle <- c(as.vector(path), 1)
          name <- paste(cycle, collapse='')
          C[[name]] <- c()
          for (i in 1:pn) {
             C[[name]] <- c(C[[name]], paste(cycle[i], cycle[i+1], sep='|'))    
          }
       } 
    }
    

    Two of the cycles found in the graph are shown below:

    C
    # $`13421`
    # [1] "1|3" "3|4" "4|2" "2|1"    
    # $`1521`
    # [1] "1|5" "5|2" "2|1"
    
  5. Now select one edge from each cycle to delete and generate a unique spanning tree:

    par(mfrow=c(4,3))
    count <- 1
    for (e1 in C[[1]]) {
       for (e2 in C[[2]]) {
          if (e1 != e2) { # make sure not to remove same edge twice
             g1 <- g %>% delete_edges(c(e1, e2))
             plot(g1, main=paste("spanning tree", count), layout = layout)
             count <- count + 1
           }
        }
     }
    

enter image description here

Sandipan Dey
  • 21,482
  • 2
  • 51
  • 63
  • Your solution is very interesting, but is it possible to share your solution on common case? For example, when the triangle 1-2-5 is absent. – Nick Nov 28 '21 at 14:36
  • 1
    @Nick if there are multiple cycles in the graph we need to remove an edge from each cycle in general to generate the spanning trees, in general. If we have n disjoint cycles with m_1, m_2 , ..., m_n edges, there will be m_1.m_2...m_n ways to remove edges, resulting in that many spanning trees. If the cycles share edges in common, we need to handle those edges separately. For example, the given graph contains 2 cycles with 3 and 4 edges, with a common edge (1,2) between them, implying we shall have 3*4-1=11 spanning trees. – Sandipan Dey Nov 28 '21 at 17:36
  • 1
    If the graph has a single cycle with m nodes in it, we can have exactly m spanning trees, since there are m ways to remove a single edge from the cycle and make the graph acyclic. – Sandipan Dey Nov 28 '21 at 17:37
  • 1
    When the triangle 1-2-5 is absent, the graph contains only one cycle of length 4, hence an edge can be removed from the cycle in 4 ways to create 4 spanning trees. – Sandipan Dey Nov 28 '21 at 18:36