Graph cycle detection with Prolog - 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).

Related

Two different paths from X to Y in a graph

I am stuck with the following Prolog question:
Given the edges of a graph with no cycles as facts. e.g:
edge(a, b).
edge(b, c).
edge(c, d).
edge(c, e).
...
I have to write a predicate that tests whether there are two different paths between vertices X and Y. e.g the call two_paths(a, c). should return true if there are two different paths from node a to node c. I know how to check whether there is a path between two vertices:
path(X, Y) :- edge(X, Y).
path(X, Y) :- edge(X, Z), path(Z, Y).
But how should I do this to check for two distinct paths? Thank you very much for your help.
An idea might be to create a predicate path/3 that returns the constructed path, and then query for two paths that are different. Something like:
path(X,Y,[X,Y]) :- edge(X,Y).
path(X,Y,[X|T]) :- edge(X,Z), path(Z,Y,T).
Now path(a,c,T) will show you the path:
?- path(a,c,L).
L = [a, b, c] ;
false.
Now you could construct a predicate:
two_paths(X,Y) :-
path(X,Y,La),
path(X,Y,Lb),
dif(La,Lb).
In other words, you ask Prolog to construct for you a path La, next construct for you a path Lb and then check if they are not equal (dif(La,Lb)). The first constructed Lb will be equivalent to La, but due to Prologs backtracking mechanism, it will try to construct you another path for which the condition might succeed. This is a rather pure Prolog implementation (with no cut (!), once/1, etc.). More efficient approaches exists since here you will "redo" the work in your second call.
A more efficient approach could be to construct a predicate path_avoid/3 (or path_avoid/4) where you feed the first constructed path to the predicate and thus force your program to at least at some point perform a different step from the one presented. I leave this open as a potential exercise.

getting prolog not to repeat answers

bigger(whale,shark).
bigger(shark,tiger).
bigger(tiger,dog).
bigger(dog,rat).
bigger(rat,ant).
bigger(rat,mouse).
bigger(cat,rat).
bigger(dog,cat).
smaller(X,Y) :- bigger(Y,X).
smaller(X,Y) :- bigger(Z,X),smaller(Z,Y).
When I ask prolog smaller(X,whale) it spits out all the correct animals but repeats several of them. can anyone tell me why and if there's a way to stop it repeating?
Some remarks first:
What kind of relation does the predicate bigger/2 describe, really?
Because of the name, we assume it is transitive: bigger(A, B) and bigger(B, C) ==> bigger(A, C)
We also can safely assume it is strict (and not reflexive): bigger(A, A) can never be true
And assymetric (not symmetric): bigger(A, B) ==> bigger(B, A) is not true
What we cannot know from the program as it stands, is if the relation describes either a total ordering, or a weak ordering: we don't know whether (a) bigger(mouse, ant), or (b) not bigger(mouse, ant), or (c) mouse and ant are equivalent (we assume they are definitely not equal)
All this just to say that you don't have a linear ordering for all the elements for which the bigger relation is defined. If you did, you could sort all animals (say bigger to smaller) and do something like:
animal_bigger(A, Bigger) :-
animal_list(Animals),
append(Bigger, [A|_Smaller], Animals),
member(A, Bigger).
Since you do not have a linear ordering, it is not possible to sort all elements and simplify the questions "what is smaller/bigger" to "what comes before/after". Instead, we need to traverse the graph described by bigger/2. At least we can safely assume that it is a directed, acyclic graph. I leave it as an exercise how to traverse a graph. To solve this problem, we can instead use library(ugraphs) (you can see the source here), and to answer, "which animals are smaller than X", we can instead ask, "which nodes are reachable from X":
Here is the complete program:
bigger(whale,shark).
bigger(shark,tiger).
bigger(tiger,dog).
bigger(dog,rat).
bigger(rat,ant).
bigger(rat,mouse).
bigger(cat,rat).
bigger(dog,cat).
:- use_module(library(ugraphs)).
animal_graph(G) :-
findall(A-B, bigger(A, B), Edges),
vertices_edges_to_ugraph([], Edges, G).
animal_smaller(A, B) :-
animal_graph(G),
reachable(A, G, R),
select(A, R, Smaller),
member(B, Smaller).
You can transpose the graph and look for reachable nodes if you want to find all elements that are bigger instead.
I hope you take the time to read this answer...
EDIT
At the end, the message is:
Your bigger/2 does not describe a list (it is not a linear ordering), and it does not describe a tree (you have more than one path to the same node). So, an algorithm that would work on a list does not work here, and an algorithm that would work on a tree does not work, either. So you either have to implement your smaller/2 to work with a graph, or use a library that can deal with graphs.
Using library solution_sequences
In recent versions of the SWI-Prolog development branch this has been made particularly easy:
?- use_module(library(solution_sequences)).
true.
?- distinct(X, smaller(X, whale)).
X = shark ;
X = tiger ;
X = dog ;
X = rat ;
X = ant ;
X = mouse ;
X = cat ;
false.
The library that allows this is documented over here.
Using library aggregate
Another way in which this can be done (also supported in older versions of SWI-Prolog):
?- aggregate_all(set(X), smaller(X, whale), Xs), member(X, Xs).
a possible standard Prolog usage:
?- X=whale,setof(Y,smaller(Y,X),L).
X = whale,
L = [ant, cat, dog, mouse, rat, shark, tiger].
I've attempted to evidence the 'functional dependencies' with the choice of symbols. When you have a list, you can use member/2 to enumerate elements.

Using freeze in JIProlog

I want to do a graph search with JIProlog. The below example works fine without the memberchk, but then it returns paths with cycles, which I don't want. When I do include it, however, Prolog freezes, probably because of the infinite search.
connected(ac,a,b). connected(ac,a,c). connected(ac,b,c).
connected(ac,b,a). connected(ac,c,a). connected(ac,c,b).
path(A,B,[AB]) :- connected(AB,A,B).
path(A,C,[H|T]) :- connected(H,A,B), path(B,C,T), \+ memberchk(H,T).
In this answer I found why (the list of edges is not yet instantiated) and a hint to a solution (using freeze/2). However, freeze/2 doesn't work in JIProlog, which is what I'm using. Can anyone help me to an alternative solution?
Edit: I know for graphs in general it would be a solution to keep track of nodes instead, such as in this example, but for my particular application the "nodes" can also be on an edge, which is why I want to check for edges that were visited rather than nodes that were visited.
I'm not sure to understand your requirement. I would try to adapt the suggested answer.
% get a path from start to end
path(Start, End, Path) :-
path(Start, End, [], Path).
path(A, B, Visited, [AB|Visited]) :- connected(AB, A, B).
path(A, C, Visited, Path) :-
connected(AB, A, B),
\+ memberchk(AB, Visited),
path(B, C, [AB|Visited], Path).
beware: untested code....

Cycle detection in a graph

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?

Return shortest path with Breadth-first search in prolog

I would like to find the shortest route from station A to station B in prolog in bidirectional graph(if A is connected to B than B is connected to A),the graph has no weights on branches. The question is posted like this
solve(Start,End,Path).
Start-starting station.
End-Destination station.
Path-List of all stations passed with the shortest route. The distance between any two directly connected stations in the graph is equal.
fact in base are like this:
fact("Staion1","metroline","Station2","metroline").
metro line is the number of line that connects the two staions directly. If 2nd and 4th argument are the same the stations are connected directly.
line("Abbesses","12","Pigalle","12").
line("Abbesses","12","Lamarck Caulaincourt","12").
line("Ale'sia","4","Mouton Duvernet","4").
line("Ale'sia","4","Porte d'Orle'ans","4").
line("Alexandre Dumas","2","Philippe Auguste","2").
line("Alexandre Dumas","2","Avron","2").
line("Alma Marcesu","9","Ie'na","9").
EDIT:
I tried to solve the problem and I figure out that it would work faster if use BFS.
here is the solution that I wrote:
solve(Start,End,Path):-solve1([Start],End,[Start],Path).
solve1([P|O],End,Visited,[End|?]):-children(P,S),member(End,S),!.
solve1([P|O],End,Visited,Path):-(not(member(P,Visited)),children(P,S),append(O,S,O1),solve1(O1,End,Visited,Path));
(solve1(O,End,Visited,Path)).
?-should be the list with path to destination Node
The only problem is that i don't know how to return the path to the destination node.
Thank's ahead.
You can use Dijkstra's algorithm.
http://en.wikipedia.org/wiki/Dijkstra's_algorithm
It begs the question, whether a breadth first algorithm has any advantage over a depth first algorithm. You anyway detect cycles via the member/2 predicate, so there is no issue of completness.
Lets say we have this graph, without any cycles:
Which can be represented in Prolog as these facts:
% edge(-Vertex, -Vertex)
edge(a, b). edge(a, c).
edge(a, d). edge(b, c).
edge(c, d). edge(c, e).
edge(d, e).
Then tabling with aggregate functions does the job:
% path(+Vertex, +Vertex, -Integer)
:- table path(_,_,min).
path(X, X, 0).
path(X, Y, N) :-
edge(X, Z), path(Z, Y, M), N is M+1.
Here are some example runs:
?- path(a, e, X).
X = 2
?- path(a, e, 3).
No
You can also modify the code, so that it detects cycles and/or returns paths. What is helpful for the later, is using custom aggregate functions.
Disclaimer: For practical purposes you wouldn't use something that boils down to Dijkstra's algorithm. You would rather use something from the A* algorithm family.

Resources