Printing the shortest path in a directed graph - prolog

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.

Related

How to write all paths in a directed graph with no cycles? (Prolog)

I'm testing out graph-searching for paths in prolog using information from https://www.irit.fr/~Jerome.Mengin/teaching/prolog/prolog-search_a4.pdf and ran into some issues with writing from recursive func.
My code can find all possible paths from node1 to node2 however the results(paths) are printed out in reverse order.
edge(a, c).
edge(a, d).
edge(c, e).
edge(e, f).
edge(d, f).
paths(Curr,Stop) :-
Curr==Stop -> write(Curr);
edge(Curr,Next),
paths(Next,Stop),
write(Curr).
For example paths(a,f) yields:
feca
true ;
fda
true.
However I want the results in the correct order acef and adf written without the use of lists.
Just remember that you are finding the path through backtracking, so the last node you arrive to, will be the first one who printed out. This forces you to write a predicate that prints the start node at the end, which means you need to solve this problem "upside down".
The idea is to use edge(Previous,Stop) instead of edge(Curr,Next).
edge(a, c).
edge(a, d).
edge(c, e).
edge(e, f).
edge(d, f).
paths(Start,Stop) :-
Start==Stop -> write(Start);
edge(Prev,Stop),
paths(Start,Prev),
write(Stop).
as you can see now, we are starting from the end and recursively trying to get to the start, when we are reaching the start the whole path is printed from start to end.

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.

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