Cycle detection in a graph - prolog

We are given a graph with the following facts:
edge(a,b)
edge(a,c)
edge(b,a)
edge(c,d)
edge(d,d)
edge(d,e)
edge(e,f)
edge(f,g)
edge(g,e)
And we are asked to define a rule, cycle(X), that determines if there is a cycle starting from the node X.
I am really lost on how to do this, I tried attempting to traverse the nodes and checking if the next one would be the starting one again but I cannot seem to get it to work

Archie's idea is a good starting point, but it will create an infinite loop if it finds another cycle while searching for the path.
I also haven't used prolog for years, but you will need something like path(X,Y,Visited), where you keep track of the visited nodes, preventing the endless loops.

I used Depth First Search with visited node list if we encounter any visited node during traversal it returns true. I tested with small inputs it looks like working correctly.
cycle(X):- cycleh(X,[X]).
cycleh(X,Visited) :- edge(X,Y), (member(Y,Visited) -> !,true; cycleh(Y,[Y|Visited])).

This should do the trick:
cycle( X ) :-
cycle( X , [] ).
cycle( Curr , Visited ) :-
member( Curr, Visited ) ,
!.
cycle( Curr , Visited ) :-
edge( Curr , Next ) ,
cycle( Next , [Curr|Visited] ) .
Although it appears to be a similar solution to #Gökhan Uras -- great minds think alike! Or something B^)
The basic logic is that you have a cycle if the current node has already been visited (the first clause in the cycle/2 helper predicate. At that point, we cut(!) and declare success The reason for the cut (!) is that without it, backtracking would result in revisiting a node already visited and thus an infinite set of cycles.
If the current node has not been visited, we grab an edge anchored at the current node and visit that. Backtracking into the 2nd clause of cycle/2 visits the next edge, so once a particular path is exhausted, cycle/2 backtracks and tries another path.

I haven't been using Prolog for some time, but here is my approach to this problem.
You could make rule path(X,Y) that checks if there exists path from node X to Y. A path is a single edge or an edge leading to a path. Having this, it's easy to find cycle starting from node X -- it will be simply path(X,X). Here is my implementation (taken from the top of my head and not necessarily correct, but gives the idea):
path(X,Y) :- edge(X,Y).
path(X,Y) :- edge(X,Z), path(Z,Y).
cycle(X) :- path(X,X).

If your Prolog system has a forward chainer, you could use it to determine cycles. But watch out, it might eat quite some memory, since it will generate and keep the path/2 facts.
Here is how you would need to formulate the rules in a forward chainer that does not eliminate automatically duplicates. The \+ is there to explicitly eliminate the duplicates:
:- forward edge/2.
:- forward path/2.
path(X,Y) :- edge(X,Y), \+ path(X,Y).
path(X,Y) :- edge(X,Z), path(Z,Y), \+ path(X,Y).
cycle(X) :- path(X,X).
To make the example a little bit more interesting what concerns the result, I have dropped the edge(d,d). Here is a sample run:
?- postulate(edge(a,b)), postulate(edge(a,c)), postulate(edge(b,a)),
postulate(edge(c,d)), postulate(edge(d,e)), postulate(edge(e,f)),
postulate(edge(f,g)), postulate(edge(g,e)), cycle(X).
X = a ;
X = b ;
X = e ;
X = f ;
X = g
The postulate/1 predicate here posts an event and keeps the propagator of the forward chainer running. How to write your forward rules depends on the Prolog systems respective library you are using.
P.S.: There is still some research going on:
http://x10.sourceforge.net/documentation/papers/X10Workshop2011/elton_slides.pdf

It's been a while since I used Prolog, but perhaps this approach will work: A path is a sequence of edges where each edge starts on the node the previous edge ended on (e.g. a -> b, b -> c, c -> d). The trivial path is a single edge, and more complex paths can be formed by taking an existing path and adding an edge to it. A cycle is a path that starts and ends on the same node. Can you use these definitions to build your Prolog rules?

Related

Graph cycle detection with Prolog

I am trying to write a prolog program that detects a cycle in an undirected graph.
I have already consulted this question: prolog graph depth first search
and have attempted to alter the dfs algorithm presented there, in order to detect cycles.
Here is my progress so far:
findCycle(Node, NextNode, Visited) :-
( member(NextNode, Visited), isNotParent(Node, NextNode) )
->
writeln("found a cycle"), saveCycle(Node, NextNode), !
;
writeln("end of findCycle").
saveCycle(Node, NextNode) :-
% save to structure
dfs(Graph, StartNode) :-
dfs(Graph, StartNode, []).
dfs(Graph, Node, Visited) :-
writeln(Visited),
\+ member(Node, Visited),
write("visiting node "),
write(Node), nl,
member(NextNode, Graph.get(Node)),
% \+ findCycle(Node, NextNode, Visited),
dfs(Graph, NextNode, [Node|Visited]).
The graph in my implementation will be represented as a dictionary, where every key is a vertex, and the corresponding value is a list of all its neighboring vertices.
Now, I have a few problems:
I need to be storing the "parent" of each vertex in a data structure, so that it can be used for cycle detection. I am not sure about how to do that. So far I have been testing the program with an example graph, whose edges I enter manually with individual terms. That is not, however, my end goal.
I also need to fix the dfs algorithm, because as it is right now, it causes stack overflow, due to the Visited list not storing all the vertices persistently. Ideally, Visited should be a dictionary instead of a list, so it can be accessed more efficiently.
Finally, if a cycle is detected, I would like to save all the vertices participating in it, into another data structure.
Though I know programming, and have written this program in C++ in the past, my grasp of prolog is at best rudimentary, so any help would be appreciated. Thanks!
You're somewhat overengineering...
Just simplify your code to the bare needed, and you'll get what you're after... for instance:
find_cycle(G,Vs) :-
member(V-_Edges,G), % don't care about starting point
find_cycle(G,V,[],Vs).
find_cycle(_G,V,SeenVs,[V|SeenVs]) :-
memberchk(V,SeenVs).
find_cycle(G,V,SeenVs,Cycle) :-
\+memberchk(V,SeenVs),
member(V-Es,G),
member(Ve,Es),
find_cycle(G,Ve,[V|SeenVs],Cycle).
?- G=[a-[b,c],b-[a,c],c-[]],find_cycle(G,C).
G = [a-[b, c], b-[a, c], c-[]],
C = [a, b, a] ;
G = [a-[b, c], b-[a, c], c-[]],
C = [b, a, b] ;
false.
Or, to avoid the duplicate search in visited edges list:
find_cycle(G,V,SeenVs,Cycle) :-
( memberchk(V,SeenVs)
-> Cycle=[V|SeenVs]
; member(V-Es,G),
member(Ve,Es),
find_cycle(G,Ve,[V|SeenVs],Cycle)
).
edit after comment...
For efficiency, note I used memberchk/2 in the test, not member/2. The implementation in SWI-Prolog it's very fast, since uses the low level representation of lists (skip list), done in C. So, you should have very long lists before you start to observe some improvements using some other data structure (there are some... like rbtrees, avltrees, ...). But if your lists are so big, then the whole algorithm should be improved. Again, there are some, you can start looking in library(ugraph).

PROLOG - Generate a list of all connected components of a graph

I learnt prolog this month at school, I'm still an absolute newbie at it.
What I'm trying to achieve is generating all connected components in one list.
My idea is to run multiple DFS on the graph, each DFS once finished will generate a list let's say LC to be appended to a result list ALL_LC.
I found some code online, which is very helpful to start with.
connected(a,b).
connected(b,e).
connected(c,d).
isConnected(X,Y) :- connected(X,Y).
isConnected(X,Y) :- connected(Y,X).
next_node(Current, Next, Path) :-
isConnected(Current, Next),
not(member(Next, Path)).
dfs(Goal, Goal, _, [Goal]).
dfs(Start, Goal, Visited, [Start|Path]) :-
next_node(Start, Next_node, Visited),
dfs(Next_node, Goal, [Next_node|Visited], Path).
First of all I thought of editing my DFS function, since I don't know the GOAL (what node trying to reach), I only have a set of roots to start with in every step. Some I'll skip since they were visited.
Any ideas how can I get to edit DFS in order to get there?

Using And/Or in Prolog

So I'm relatively new to prolog and I've came across some code on the internet which can be used to represent an And/Or node.
% a is an or node with successors b,c,d.
or(a,[b,c,d]).
% c is an and node with successor d,e,f.
and(c,[d,e,f]).
% d is a terminal (solvable) node.
goal(d).
I'm confused as to how this predicates could be used to find a solvable node.
And references to point me in the right direction would be marvelous
You appear to have found question 2 (c) from the University of East Anglia's School of Computing Sciences Main Series UG Examination 2013/14 (found using a quoted text search on Google), asking:
Given an AND/OR tree specified by a set of Prolog clauses of the form
% a is an or node with successors b,c,d.
or(a,[b,c,d]).
% c is an and node with successor d,e,f.
and(c,[d,e,f]).
% d is a terminal (solvable) node.
goal(d).
write a Prolog program whose main clause, solve(X), succeeds if and only if X is solvable.
As background:
An and-node is solvable if and only if all of its successors are solvable.
An or-node is solvable if and only if at least one of its successors is solvable.
A terminal node is solvable if it is a goal.
The Prolog program requested could look something like this:
%not necessary - without this solve_and([]) will be false anyway
solve_and([]) :-
false.
solve_and([H]) :-
solve(H).
solve_and([H|T]) :-
solve(H),
solve_and(T).
%not necessary - without this solve_or([]) will be false anyway
solve_or([]) :-
false.
solve_or([H|T]) :-
solve(H);
solve_or(T).
solve(X) :-
goal(X),
!.
solve(X) :-
and(X, A),
solve_and(A),
!.
solve(X) :-
or(X, A),
solve_or(A),
!.
This works nicely from the perspective of a consumer - one that calls solve with X already grounded to check for correctness, but the cuts (!) make it a poor generator of solvable Xs. Removing the cuts from the solve rules should make the system bidirectional.
This particular problem wants you to write a predicate to determine if a given node is solvable. In other words, if you were to query solve(z) it would succeed or fail depending upon whether z were solvable.
You would start by writing out what the rules are. Jim Ashworth already did this in his answer, but I'll restate them here:
Rule 1: Node X is solvable if node X is a goal
Rule 2: Node X is solvable if X is the conjunction (and) of one or more nodes that are all solvable
Rule 3: Node X is solvable if X is the disjunction (or) of one or more nodes, at least one of which is solvable
Let's start by simply writing this in Prolog.
% Rule 1
solve(X) :- goal(X).
% Rule 2
solve(X) :-
and(X, Subgoals),
all_solvable(Subgoals).
% Rule 3
solve(X) :-
or(X, Subgoals),
at_least_one_solvable(Subgoals).
Now we need to write predicates all_solvable/1 and at_least_one_solvable/1:
% Auxiliary predicates used above
all_solvable([]).
all_solvable([Node|Nodes]) :-
solve(Node),
all_solvable(Nodes).
at_least_one_solvable([Node]) :- solve(Node).
at_least_one_solvable([Node, NextNode|Nodes]) :-
( solve(Node)
-> true
; at_least_one_solvable([NextNode|Nodes])
).
You can see that this is almost the same as Jim's answer, which was completely in the right direction. I'm just providing some improvements from a Prolog perspective. For this reason, I think Jim deserves the credit for the answer. Differences, besides my choice of predicate names, are:
I omitted absolutely failing goals as being superfluous
I used the p1 -> p2 ; p3 construct to handle the "succeed once" for the disjunctive case
The all_solvable/1 will succeed even if there are no subgoals (most general solution)
I avoided cuts otherwise to allow for the general solutions to occur
Above, the p1 -> p2 ; p3 construct behaves as p1, !, p2 ; p3. So for some cases, the cut is helpful, as Jim pointed out in his comment, that you are only looking for solvability once in this problem, not multiple ways of solvability. You can also find a way to use the once/1 predicate to achieve this (you can look that one up as an exercise).
An alternative implementation for all_solvable/1:
all_solvable(Goals) :- maplist(solve, Goals).
Here, maplist(solvable, Goals) will succeed if and only if solvable succeeds for every element of the Goals list.
As with Jim's solution, this will tell you if a specific goal is solvable (although Jim's answer won't leave a choice point as my solution does):
| ?- solve(a).
true? ;
no
| ?-
An additional benefit of the above solution is that it answers the general query properly with all of the correct solutions:
| ?- solve(X).
X = d ? ;
X = a ? ;
no
| ?-

Printing the shortest path in a directed graph

I have a directed, cyclic graph without weights. I want to find the shortest route between A and B (i.e. the one with the least hops).
This is the code i got so far:
path(A,B) :- walk(A,B,[]).
walk(A,B,V) :- edge(A,X), not(member(X,V)), (B=X); walk(X,B,[A|V]).
edge(a, b).
edge(b, c).
edge(a, d).
edge(c, a).
This code prints true, once for every route it finds. How can i print the path? And what would i have to do to find the path with the least hops?
You need to unify an extra argument with what you have accumulated in V once you reach your termination condition:
path(A,B,P) :- walk(A,B,[],P).
walk(B,B,V,P) :- reverse(V,P).
walk(A,B,V,P) :- dif(A,B), edge(A,X), maplist(dif(X),V), walk(X,B,[A|V],P).
Once A and B are the same, it means that we don't have to walk the graph anymore. In that case we reverse the path that we accumulated in V as P, which gets "returned" from path/3.
Note: it is almost always clearer to put the code that checks the recursion termination as a separate rule, instead of using ;.
To find the path with the least hops, you can find all paths from the two points you want, and then take the smallest one:
shortest_path(A,B,S) :-
findall(P, path(A,B,P), Ps),
maplist(prepend_length, Ps, Ls),
sort(Ls, [[_,S]|_]).
prepend_length(P, [L,P]) :-
length(P,L).
To yield the path, you'd need to add an argument to keep track of it; for example:
path(A,B,P) :- walk(A,B,[],P).
walk(A,B,V,[A,B]) :- edge(A,X), not(member(X,V)), (B=X).
(I'll leave the recursive case as an exercise.)
To find the shortest path, you could find them all (findall) & pick out the shortest.

Prolog Connected Edge of a graph

I want to make a predicate that check if a node can reach another node in graph in prolog. e.g Connected(1,X,[[1,3],[3,4],[2,5]]) The first argument is the node I want to start the second is the node I want to reach and the third is a list of edges. So far I have managed to do it but I get an infinite loop when I try to get all the nodes I reach by using findall/3.
Is there any way to stop the infinite loop or I should thing the problem from the beggining?
Here is my code so far:
match([X,Y],List):- member([X,Y], List).
match([X,Y],List):- member([Y,X], List).
go(X,Y,List):-match([X,Y],List).
go(X,Y,List):-match([X,Z],List),go(Z,Y,List).
goes(X,List,List2):-findall(Y,go(X,Y,List),List2).
I am new in Prolog and I cannot figure out what I am doing wrong.
a possible correction to your code
match([X,Y], List, Rest):- select([X,Y], List, Rest).
match([X,Y], List, Rest):- select([Y,X], List, Rest).
go(X,Y,List) :- match([X,Y],List,_).
go(X,Y,List) :- match([X,Z],List,Rest), go(Z,Y,Rest).
goes(X,List,List2) :- findall(Y, go(X,Y,List), List2).
now match 'consumes' an edge, then avoid infinite looping
Have a look at what ?- gtrace, goes(1,[[1,3],[3,4],[2,5]], X). Does.
For all recursions go :- go you need a condition when it stops.
In you case I recommend to add an argument Traversed which is a list of all nodes(or edges) you have visited. Then you do not go over nodes again if you have visited them already.
So you get less and less elements to recursively look at and the recorsion ends at some point.
Many recursions use the predicate !. Have a look at it.

Resources