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....
Related
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).
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.
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.
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.
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.