Find path in graph by backtracking using prolog - prolog

I have the following graph:
maniere(v11,h11).
maniere(h11,v12).
maniere(h11,h21).
maniere(v12,v22).
maniere(v22,h13).
maniere(v22,h23).
maniere(h13,v21).
maniere(v22,h23).
maniere(h12,h22).
maniere(h23,v23).
maniere(h33,v23).
maniere(v13,h32).
maniere(v23,h32).
The required output is:
?- traverser(v11, v23).
v11 to h11
h11 to v12
v12 to v22
v22 to h13
h13 to v21
v22 to h23
h23 to v23
true .
I'm having trouble when I don't format the output as requested.

The general rule for traversing a graph is pretty simple:
The simple/special case:
We can get from A to B if A and B are directly connected.
The more general/recursive case:
We can get from A to B if A is connected to some intermediate node X, and we can then get from X to B.
Correct?
That can be expressed directly in Prolog.
For the sake of argument we will use edge/2 to define our graph, so edge(a,b). is a fact that tell us that we can travel from a to b (but not from b to a — our graph is a directed graph.
traverse(A,B) :- edge(A,B).
traverse(A,B) :- edge(A,X), traverse(X,B).
To get what you need, though, you'll want to track your path through the graph. To do that, you need to use a helper predicate that takes additional arguments to carry state and return results, The other reason for tracking your path through the graph is so that you can detect cycles in the graph — if you wind up back at a node you've already visited, you'll wind up going into an infinite loop (or at least, until the stack overflows). That gets you to this:
traverse(A,B) :- % we can get from A to B iff...
traverse(A,B,[A]). % - we invoke the helper, seeding the list of visited nodes with A, the origin node
% ---------------------------------------
% traverse(Origin, Destination, Visited )
% ---------------------------------------
traverse( A , B , _ ) :- % we can get from A to B iff...
edge(A,B). % - A and B are directly connected.
traverse( A , B , V ) :- % Otherwise, we can get from A to B iff...
edge(A,X), % - An edge exists from A to X, and
not_visited(X,V), % - We have not yet visited X, and
traverse(X,B,[X|V]). % - We can get from X to B
not_visited(N,V) :- % we haven't visited node N...
\+ member(N,V). % - if N is not contained in the list of visited nodes (V).
But you need to return the complete path, which is just a matter of adding an additional argument to the helper predicate:
traverse(A,B,P) :- % we can get from A to B iff...
traverse(A,B,[A],P). % - we invoke the helper, seeding the list of visited nodes with A, the origin node
% ---------------------------------------
% traverse(Origin, Destination, Visited )
% ---------------------------------------
traverse( A , B , V, [B|V] ) :- % we can get from A to B iff...
edge(A,B). % - A and B are directly connected.
traverse( A , B , V, P ) :- % Otherwise, we can get from A to B iff...
edge(A,X), % - An edge exists from A to X, and
not_visited(X,V), % - We have not yet visited X, and
traverse(X,B,[X|V],P). % - We can get from X to B
not_visited(N,V) :- % we haven't visited node N...
\+ member(N,V). % - if N is not contained in the list of visited nodes (V).
And then, you need a way to display the path.
You might notice that the path is returned in reverse order. You can use recursion to deal with that:
display_path([]).
display_path([_]).
display_path([To,From|Ns]) :-
display_path([From|Ns]),
display_leg(From,To).
display_leg(From,To) :- write(From), write(' --> '), write(To), nl.
But that's a little counter-intuitive. Better to have the helper reverse the list on success:
traverse(A,B,P) :- % we can get from A to B iff...
traverse(A,B,[A],P). % - we invoke the helper, seeding the list of visited nodes with A, the origin node
% ---------------------------------------
% traverse(Origin, Destination, Visited )
% ---------------------------------------
traverse( A , B , V, P ) :- % we can get from A to B iff...
edge(A,B), % - A and B are directly connected,
reverse([B|V],P).
traverse( A , B , V, P ) :- % Otherwise, we can get from A to B iff...
edge(A,X), % - An edge exists from A to X, and
not_visited(X,V), % - We have not yet visited X, and
traverse(X,B,[X|V],P). % - We can get from X to B
not_visited(N,V) :- % we haven't visited node N...
\+ member(N,V). % - if N is not contained in the list of visited nodes (V).
That makes display_path/1 much more intuitive:
display_path([]).
display_path([_]).
display_path([From,To|Ns]) :-
display_leg(From,To),
display_path([To|Ns]).
display_leg(From,To) :-
write(From),
write(' --> '),
write(To),
nl.
And we can wrap our traverse/3 up with display_path/1:
visit(A,B) :- traverse(A,B,P), display_path(P).
Wrapping it all up, we get this (you can fiddle with it at https://swish.swi-prolog.org/p/rmEdFAqE.pl):
edge(a,b).
edge(a,c).
edge(b,c).
edge(c,d).
visit(A,B) :- traverse(A,B,P), display_path(P).
traverse(A,B,P) :- % we can get from A to B iff...
traverse(A,B,[A],P). % - we invoke the helper, seeding the list of visited nodes with A, the origin node
% ---------------------------------------
% traverse(Origin, Destination, Visited )
% ---------------------------------------
traverse( A , B , V, P ) :- % we can get from A to B iff...
edge(A,B), % - A and B are directly connected,
reverse([B|V],P).
traverse( A , B , V, P ) :- % Otherwise, we can get from A to B iff...
edge(A,X), % - An edge exists from A to X, and
not_visited(X,V), % - We have not yet visited X, and
traverse(X,B,[X|V],P). % - We can get from X to B
not_visited(N,V) :- % we haven't visited node N...
\+ member(N,V). % - if N is not contained in the list of visited nodes (V).
display_path([]).
display_path([_]).
display_path([A,B|Ns]) :-
display_leg(A,B),
display_path([B|Ns]).
display_leg(From,To) :-
write(From),
write(' --> '),
write(To),
nl.

Related

how to find all connected path from node to another node using prolog?

how can i find all path of node that are connected between : a to g
by using rules on Prolog ?
The graph
my simple code:
cites(a,c).
cites(a,c).
cites(b,d).
cites(b,e).
cites(c,f).
cites(e,g).
cites(f,g).
cites(g,d).
cites(h,g).
connected(A,B):-cites(A ,B).
connected(A,B):-cites(A ,C),connected(C ,B).
The first thing you need is a way to generate or test routes. I would do that like this. It will successively find all possible routes via backtracking:
cities(a,b).
cities(a,c).
cities(b,d).
cities(b,e).
cities(c,f).
cities(e,g).
cities(f,g).
cities(g,d).
cities(h,g).
% ------------------------------------------
%
% route( Origin, Destination, Route)
%
% Does Route connect Origin and Destination?
%
%-------------------------------------------
route( A , B , R ) :- % find route R between A and B by...
route( A , B , [A] , P ) , % - invoke the helper, seeding the list of visited nodes with the origin
reverse(R,P) % - and reversing the path to get the route.
. % Easy!
% -----------------------------------------------------------
%
% route( Origin, Destination, Visited, Path )
%
% Finds the path from Origin to Destination, using Visited to
% detect cycles in the graph, building out the final route
% in reverse order.
% -----------------------------------------------------------
route( A , B , Vs , [B|Vs] ) :- % We can get from A to B if...
cities(A,B) , % - A is directly connected to B, and
not_yet_visited(B,Vs) % - we've not yet visited B
. % otherwise...
route( A , B , Vs , Rs ) :- % we can get from A to B if...
cities(A,C) , % - A is connected to some city C, and
not_yet_visited(C,Vs) , % - we've not yet visited C, and
route(C,B,[C|Vs],Rs) % - C is [recursively] connected to B
. % Easy!
%----------------------------------------------------------
%
% not_yet_visited( Node, Visited )
%
% succeeds if Node is not found in Visited; false otherwise
%
%----------------------------------------------------------
not_yet_visited( X , Xs ) :- \+ memberchk(X,Xs) .
Once you have that, then it's a simple matter of using findall/3, bagof/3 or setof/3 to collect all solutions in a list of lists. Something like:
all_routes( A , B , Rs ) :- findall( route(A,B,R), route(A,B,R), Rs ) .
Following short and simple code is adapted from here.
cities(1,2).
cities(2,3).
cities(3,4).
cities(3,5).
cities(2,5).
cities(5,6).
cities(2,6).
connected(X,Y,[cities(X,Y)]) :- cities(X,Y).
connected(X,Y,[cities(X,Z)|P]) :- cities(X,Z),connected(Z,Y,P).
Then one can search for paths:
?- connected(1,6,P).
P = [cities(1, 2), cities(2, 6)] ;
P = [cities(1, 2), cities(2, 3), cities(3, 5), cities(5, 6)] ;
P = [cities(1, 2), cities(2, 5), cities(5, 6)] ;

Swi-Prolog: How to write a descendant predicate?

I have the provided definition of descendent as below:
"a person who is in direct line to an ancestor. Eg: child,
grandchild, great-grandchild, and forever"
and also other rules like:
...
childof(X, Y).
parent(X, Y).
grandchild(X, Y).
ancestor(X, Y).
...
So can I just simply write a rule as below,
descendant(X, Y):- ancestorof(Y, X).
or is there a better way of doing it?
This is a problem of tree traversal or graph traversal.
I would generalize an ancestor_of/3 predicate:
ancestor(Ancestor,Generations,Descendant)
Assuming you have, say, a set of facts like
parents( mother, father, child ).
On can say that a parent is either the mother or father of a child:
% P is a parent of C if P is either the mother or father of C.
parent(P,C) :- parents(P,_,C).
parent(P,C) :- parents(_,P,C).
And given the above, the solution might look like this:
ancestor(Ancestor, Generations, Descendant) :-
ancestor( Ancestor, 0, Generations, Descendant )
.
ancestor( A, N, G, D ) :- % A is an ancestor of D...
parent(A,D), % * if A is the immediate parent of D
G is N+1 % * in which case, tick the generation count
. % Easy!
ancestor( A, N, G, D ) :- % otherwise, A is an ancestor of D...
parent(A,X), % * if A is the immediate parent of somebody
X \= D, % * other than D
N1 is N+1, % * [ tick the generation count]
ancestor( X, N1, G, D ) % and that somebody is the ancestor of D
. % Also easy!
That allows us to say things like:
grandparent(A,C) :- ancestor(A,2,C).
great-grandparent(A,C) :- ancestor(A,3,C).
etc.
This will work for biological parentage, but if you are allowing
non-biological parentage (e.g., step-parents and the like, then
"I'm my own grandpa" is a possibility, and you will need to carry
a list of visited nodes around so you can detect any cycles in the
graph.
Try to figure out how to determine different degrees of kinship from this
(what does a second cousin thrice removed look like?). You'll need to determine sibling status for starters.

Enumerating all walks in a graph

Given a term representation of an edge of a graph, i.e.:
edge(a, b).
edge(b, c).
I would like to construct a predicate path/1 which succeeds iff its sole argument is a valid path in this graph (that is, for every two adjacent terms X, Y, edge(X, Y) holds). Given a variable, it should enumerate all walks (which could have repeated nodes). My first try:
path([X, Y]) :- edge(X, Y).
path([X, Y, Z | T]) :-
path([Y, Z | T]),
edge(X, Y).
It works as intended except for the case where it is supplied an acyclic graph - path finds all solutions and then halts, unable to construct any other path. On the other hand, swapping first and second term will result in many walks being skipped, due to the DFS nature of Prolog resolution.
My second attempt:
path(P) :- length(P, L), L >= 2, (path(P, L) *-> true ; (!, fail)).
path([X, Y], 2) :- edge(X, Y).
path([X, Y, Z | T], L) :-
L >= 3,
L1 is L - 1,
edge(X, Y),
path([Y, Z | T], L1).
It works as intended, but using a soft cut feels a bit forced. I was wondering if there was an easier way to accomplish this, perhaps a simpler simulation of a soft cut is possible in this particular scenario?
Testing your first solution, after finding three paths ([a,b],[a,c],[a,b,c]), it loops. One super quick way to avoid this is to use tabling wich is available in XSB, SWI and YAP. In case of SWI just add :- table path/1. as first directive to avoid loops. Otherwise you need to remember all path and there are plenty of answers you can look at (like this).
Given your graph definition:
edge(a, b).
edge(b, c).
Something like ought to do you:
path(P) :-
node(X),
walk(X,_,P)
.
walk(A,B,P) :-
walk(A,B,[],V),
reverse(V,R),
P = [A|R]
.
walk( A, B, T, V ) :-
edge(A,X),
not( member(X,T) ),
(
( B = X , V = [B|T] )
;
walk( X, B, [A|T], V )
)
.
%
% enumerate the distinct nodes in edge/2 via backtracking
%
node(N) :-
setof( X , edge(X,_);edge(_,X) , Ns ),
node( Ns , N )
.
node( [N|_] , N ).
node( [_|Ns] , N ) :- ( Ns , N ).

Prolog: If Two Nodes Can Be Traversed To In A Graph

Here's the Question I was given:
Define a Prolog predicate path(X,Y,G), where path(-,-,+), which is
true when there is a path from node X to node Y in a directed graph G,
where the graph is represented by a list of edges, each represented by
a two-element list of the source and destination nodes.
Here's the sample output:
?- path(b,Y,[[a,b],[b,c],[b,d],[d,e]]).
Y = c ;
Y = d ;
Y = e ;
no
?- path(X,b,[[a,b],[b,c],[b,d],[d,e]]).
X = a ;
no
?- path(c,e,[[a,b],[b,c],[b,d],[d,e]]).
yes
This is different to other examples I've seen online where the node traversals would be facts such as:
canTraverse(a,b).
canTraverse(b,c).
etc.
So I'm pretty stumped with it.
This is what I've gotten out so far:
path(X, Y, G) :-
(
G = [CurrentPair | EverythingElse],
CurrentPair = [X1 , Y1| _],
=(X, X1),
=(Y, Y1)
)
;
path(X, Y, EverythingElse).
Which seemed to work if the two nodes X and Y were in a pair/list together. But I'm not sure how to get it to traverse across nodes.
The graph is directed and doesn't have cycles.
The base case could be path(X, Y, G) :- member([X,Y],G). and the recursive case can say for there to be a path from X to Y, take a step from X to some middle note then find a path from middle to Y.

Reversing a list

I need help reversing a list.
fun(a, [b, d]).
fun(b, [c]).
fun(c, []).
fun(d, [e]).
fun(e, [f]).
fun(f, [g]).
fun(g, []).
xyz(X, Y):-
fun(X, Z) -> findall([A|B], (member(A, Z), xyz(A, B)), L),
flatten(L, F), sort(F, Y); Y = [].
The query xyz(a,X). gives me X = [b,c,d,e,f,g].
However, I would like it to give me X = [g,f,e,d,c,b].
I have tried different attempts at reversing the list, but I am not having any luck.
I have tried adding an additional predicate right after this, but it didn't work either:
xyz2(X,Y):-
xyz(X,Y),
reverse(Y,Z),
Z\=0.
Credit goes to CapelliC for the approach to the implementation above found at my other post here.
Recursion in PROLOG?
You can avoid some difficult programming, and make your program easier to read by re-defining your problem. Say the f/2 describes a directed graph, with edges from the first argument to each of the elements in the second argument, so:
a ---> b
a ---> d
b ---> c
% etc
Then, your question is, which nodes in the graph are reachable from a given node? You can define the solution with the help of library(ugraphs).
To make all edges from an f/2:
edge(From-To) :-
f(From, L),
member(To, L).
You can now collect the edges, make a graph, and find which nodes are reachable from a starting node:
foo(X, L) :-
findall(E, edge(E), Edges),
vertices_edges_to_ugraph([], Edges, G),
reachable(X, G, All),
once(select(X, All, R)), % remove the node you start from
reverse(R, L).
Per definition, a node is always reachable from itself, so you need to pick it out of the list of reachable nodes.
?- foo(a, X).
X = [g, f, e, d, c, b].
?- foo(e, X).
X = [g, f].
?- foo(g, X).
X = [].
I don't exactly understand why the order of the elements is significant. This feels a bit like a code smell.

Resources