In an undirected graph, the edge to the parent of a node should not be counted as a back edge
So, a cycle is a path with at least three nodes (and the next node after the last node of the path must be the start node of the path).
To avoid duplicate cycles, a list representing a cycle is normalized by rotating its smallest node to the beginning of the list, followed by its nearest neighbor in the cycle (according to the order of its labels). For example, both lists [3,2,1]
and [2,3,1]
are normalized to [1,2,3]
.
find_cycles(Graph, Cycles) :-
setof(Cycle, Graph^find_cycle(Graph, Cycle), Cycles).
find_cycle(Graph, Cycle) :-
find_cycle([Start], Start, Graph, Cycle).
find_cycle([Node|Path], Start, Graph, Cycle) :-
member(Node-Neighbors, Graph),
member(Next, Neighbors),
( memberchk(Next, Path) % back edge
-> Next == Start,
Path = [_,_|_], % at least two more nodes
normalize_cycle([Node|Path], Cycle)
; Next @> Start, % Start is the smallest node of the cycle
find_cycle([Next,Node|Path], Start, Graph, Cycle) ).
normalize_cycle([End|Path], Normalized) :-
reverse([End|Path], [Start,Next|Rest]),
( Next @< End
-> Normalized = [Start,Next|Rest]
; reverse([Next|Rest], Reversed),
Normalized = [Start|Reversed] ).
graph(0, [a-[b,c,d], b-[a,c], c-[a,b,d], d-[a,c]]).
graph(1, [a-[b,c,d], b-[a,c], c-[a,b,d], d-[a,c], e-[f,g], f-[e,g], g-[e,f]]).
graph(2, [1-[2,3,4], 2-[1,3,5], 3-[1,2,4], 4-[1,3,5], 5-[2,4], 6-[7,8], 7-[6,8], 8-[6,7]]).
Examples:
?- graph(0, G), find_cycles(G, Cs).
G = [a-[b, c, d], b-[a, c], c-[a, b, d], d-[a, c]],
Cs = [[a, b, c], [a, b, c, d], [a, c, d]].
?- graph(1, G), find_cycles(G, Cs).
G = [a-[b, c, d], b-[a, c], c-[a, b, d], d-[a, c], e-[f, g], f-[e, g], g-[e|...]],
Cs = [[a, b, c], [a, b, c, d], [a, c, d], [e, f, g]].
?- graph(2, G), find_cycles(G, Cs), maplist(writeln, Cs).
[1,2,3]
[1,2,3,4]
[1,2,5,4]
[1,2,5,4,3]
[1,3,2,5,4]
[1,3,4]
[2,3,4,5]
[6,7,8]
G = [1-[2, 3, 4], 2-[1, 3, 5], 3-[1, 2, 4], 4-[1, 3, 5], 5-[2, 4], 6-[7, 8], 7-[6|...], 8-[...|...]],
Cs = [[1, 2, 3], [1, 2, 3, 4], [1, 2, 5, 4], [1, 2, 5, 4, 3], [1, 3, 2, 5|...], [1, 3, 4], [2, 3|...], [6|...]]