Reputation: 6650
I'm using python and graph-tool to do graph analysis. In a graph g
, I want to find all cycles up to a specific length max_length
of which a given node node
is a member.
Related question:
How to iterate simple cycles in ascending cycle length order? is a broader question, which is more general and about a broader aspect. These questions act on different levels of abstraction and they are not duplicates of each other.
I've tried graph_tool.topology.all_circuits
, but that runs far too long. It finds way too many cycles, e.g. for a graph with just 34 nodes, it finds 1462130 many cycles. In practice, I want to analyse a graph with more than 2 million nodes and there it would probably run for years. Here is an example to test this:
import graph_tool.collection
import graph_tool.topology
g = graph_tool.collection.data["karate"]
print("number of nodes in graph:", g.num_vertices())
print()
print(f"circ_idx\tcircuit")
circuit_iter = graph_tool.topology.all_circuits(g)
i = 0
for circuit in circuit_iter:
i += 1
if i % 152371 == 0:
print(f"{i}\t{circuit}")
print("total number of circuits:", i)
Output:
number of nodes in graph: 34
circ_idx circuit
152371 [ 0 3 1 13 2 28 31 25 24 27 33 29 23 32 8]
304742 [ 0 7 1 30 32 23 33 27 24 25 31 28 2 3 12]
457113 [ 0 8 30 1 19 33 18 32 29 23 25 24 31]
609484 [ 0 8 33 27 24 25 23 29 32 2 3]
761855 [ 0 13 1 7 3 2 32 31 25 23 33 19]
914226 [ 0 17 1 7 3 2 32 31 24 27 23 29 33 30 8]
1066597 [ 0 19 33 27 24 25 23 29 32 30 1 3]
1218968 [ 0 31 24 27 23 29 26 33 22 32 2 3 12]
1371339 [ 0 31 32 23 25 24 27 33 13 3 2 1 30 8]
total number of circuits: 1462130
Therefore, I think that the best approach is to use breadth first search (BFS), starting at node node
, to find the cycles up to a specific length of which this node is a member, and stopping the BFS when the max_length
is reached. I've tried that using graph_tool.search.bfs_iterator and graph_tool.search.bfs_search, but surprisingly, neither of them can be used to find cycles at all, as can be seen from the output of the following program:
import graph_tool.collection
import graph_tool.search
g = graph_tool.collection.data["karate"]
print("number of nodes in graph:", g.num_vertices())
print()
print("adjacencies")
for src in range(5):
neighbors_list = g.get_all_neighbors(src)
neighbors_list.sort()
print(" ", src, neighbors_list)
print(" ...")
node = 0
print()
print("graph_tool.search.bfs_iterator")
bfs_iter = graph_tool.search.bfs_iterator(g, node)
for edge in bfs_iter:
src = g.vertex_index[edge.source()]
tar = g.vertex_index[edge.target()]
found = " ############################## cycle found!!!" if tar == node else " no cycle found"
print(" ", src, tar, found)
Output:
number of nodes in graph: 34
adjacencies
0 [ 1 2 3 4 5 6 7 8 10 11 12 13 17 19 21 31]
1 [ 0 2 3 7 13 17 19 21 30]
2 [ 0 1 3 7 8 9 13 27 28 32]
3 [ 0 1 2 7 12 13]
4 [ 0 6 10]
...
graph_tool.search.bfs_iterator
0 1 no cycle found
0 2 no cycle found
0 3 no cycle found
0 4 no cycle found
0 5 no cycle found
0 6 no cycle found
0 7 no cycle found
0 8 no cycle found
0 10 no cycle found
0 11 no cycle found
0 12 no cycle found
0 13 no cycle found
0 17 no cycle found
0 19 no cycle found
0 21 no cycle found
0 31 no cycle found
1 30 no cycle found
2 9 no cycle found
2 27 no cycle found
2 28 no cycle found
2 32 no cycle found
5 16 no cycle found
8 33 no cycle found
31 24 no cycle found
31 25 no cycle found
27 23 no cycle found
32 14 no cycle found
32 15 no cycle found
32 18 no cycle found
32 20 no cycle found
32 22 no cycle found
32 29 no cycle found
33 26 no cycle found
The graph_tool.search.bfs_search together with a BFSVisitor is another approach to BFS, but it has the same obstacle for detecting cycles, and it is thus unusable for my purpose. (Update: I doubt my own assessment here. See comment.)
Do I really have to implement BFS on my own in order to use a BFS for cycle detection? It's not tons of work, but I wonder if there is a better alternative. Moreover, implementing it in python will make it slow.
Upvotes: 0
Views: 118