The graph is shown in the figure below. I have to tell whether there is path between the two nodes and print the path also. For instance: my query is path(a,c). then it will return True. Now when i am giving the query as path(g,f), Ist and 2nd results are True and after that instead of terminating, its starts looping around. I am a novice in prolog. Please suggest some solution for stopping the recursion after getting the correct number of paths
These are the facts
arc(a,b).
arc(b,c).
arc(c,d).
arc(d,b).
arc(b,g).
arc(g,b).
arc(d,g).
arc(g,d).
arc(f,d).
arc(a,e).
arc(f,e).
arc(a,d).
arc(f,g).
arc(c,f).
path(X,Y):- arc(X,Y).
path(X,Y):- arc(X,Z),path(Z,Y).
You are traversing a directed graph. Assuming that it is acyclic, this should enumerate all the possible paths:
path(X,Y) :- % a path exists from X to Y
arc(X,Y) % IF an edge exists *from* X *to* Y
. %
path(X,Y) :- % Otherwise, a path exists from X to Y
arc(X,Z) , % IF an outbound path from X exists
Z \== Y , % whose destination (Z) is NOT Y, and
path(X,Y) % a path exists from Z to Y
. % Easy!
We're using \==/2 here because this predicate might be invoked with unbound argument(s).
Another way to write it might be:
path(X,Y) :- % A path exists from X to Y
arc(X,Z) , % IF an outbound path from X exists,
( % AND
Z = Y % Y is that destination (Z)
; % OR
path(Z,Y) % Y can be reached from Z.
) .
If your graph is cyclic, you'll need to build and carry along with you as you go a list of visited nodes, so you can skip visiting nodes you've already seen, lest you go down the rabbit hole of infinite recursion.
Related
This is my code:
% A The first link as a predicate
link(1,2).
link(2,3).
link(3,4).
link(3,6).
link(6,7).
link(6,5).
So what we did with the path predicate is check from a given starting point check if there exists a path from that point to the goal (which is defined at the top). This gives the correct outcome for all possible values.
What I need to do now is, I know there is a valid path from 1 to the goal, my path() predicate told me so, now i need to return a list of nodes that shows the that path to the goal, so with using path(L), path([2,3,6,5]) is true.
If I understand your problem statement, you
Have a directed graph (a digraph) defined by link(From,To).
Wish to be able to traverse that digraph from some origin node to some destination node and identify the path[s] between the 2 nodes.
This is a pretty straightforward problem that doesn't require assert/1 or retract/1.
A common pattern in Prolog programming is the use of helper predicates that carry extra arguments that track the persistent state required to accomplish the task at hand.
Graph traversal, as an algorithm, is pretty easy (and nicely recursive):
You can travel from node A to node B if nodes A and B are directly connected, or
You can travel from node A to node B if node A is connected to some other node X such that you can travel from node X to node B.
The trick here is that you need to track what nodes you've visited (and their order), and you'd want to do that anyway, so that you can detect cycles in the graph. Trying to traverse a cyclic graph like this::
a → b → c → b
leads to infinite recursion unless you check to see whether or not you've already visited node b.
That leads pretty direction to an implementation like this:
A traverse/3 predicate, traverse(Origin, Destination,Path), and,
A traverse/4 helper predicate, traverse(Origin,Destination,Visited,Path).
You can fiddle with it at https://swish.swi-prolog.org/p/oZUomEcK.pl
link(1,2).
link(2,3).
link(3,4).
link(3,6).
link(6,7).
link(6,5).
% ---------------------------------------------------------------------------
%
% traverse( Origin, Destination, ReversePath )
%
% traverse/3 traverses a directed graph defined by link(From,To).
% The path taken is listed in reverse order:
% [Destination, N3, N2, N1 , Origin]
%
% ---------------------------------------------------------------------------
traverse(A,B,P) :- % to traverse a graph
traverse(A,B, [], P) % - invoke the helper, seeding the visited list with the empty list
. %
% ---------------------------------------------------------------------------
% traverse( Origin, Destination, Visited, ReversePath )
% ---------------------------------------------------------------------------
traverse( A , B , V , [B,A|V] ) :- % Traversal of a graph from A to B,
link(A,B) % - succeeds if there exists an edge between A and B.
. %
traverse( A , B , V , P ) :- % otherwise, we can get from A to B if...
link(A,X) , % - an edge exists between A and some node X
\+ member(X,V) , % - that we have not yet visited,
traverse(X,B,[A|V],P) % - and we can get from X to B (adding A to the list of visited nodes
. %
Once you have that, invoking traverse/3 with all arguments unbound
?- traverse(A,B,P) .
results in finding all paths in the graph via backtracking.
If you want to know all the paths in the graph, beginning at a specific origin node, invoke it with the origin argument bound:
?- traverse(1,B,P) .
And if you want to know if two specific nodes are connected, invoke it with both origin and destination bound:
?- traverse(1,5,P) .
Note: because we're building the list of visited nodes by prepending them to the list of visited nodes, the path thus build is in reverse (destination-first) order. If you want the path in proper (origin-first) order, just use reverse/2 in the main predicate:
traverse(A,B,P) :- traverse(A,B, [], RP ), reverse(RP,P) .
I have defined the following network:
connected(d,f).
connected(d,a).
connected(d,b).
connected(d,c).
connected(c,a).
connected(c,b).
connected(c,e).
connected(b,e).
connected(X,Y) :-
edge(X,Y).
connected(X,Y) :-
edge(Y,X).
I need to find all paths between two specified nodes and write them out. I have found a previous post that gives a way to check if there is a path between two nodes but I cannot think of a way how to write out the paths.
path(A,B) :- % two nodes are connected, if
walk(A,B,[]) % - if we can walk from one to the other,
. % first seeding the visited list with the empty list
walk(A,B,V) :- % we can walk from A to B...
edge(A,X) , % - if A is connected to X, and
not(member(X,V)) , % - we haven't yet visited X, and
( % - either
B = X % - X is the desired destination
; % OR
walk(X,B,[A|V]) % - we can get to it from X
) %
. % Easy!
I am a beginner in Prolog and I have a task to do.
I need to check if the graph is connected.
For now I have that...
graph(
[arc(a,b)],
[arc(a,f)],
[arc(b,c)],
[arc(c,d)],
[arc(c,e)],
[arc(e,d)],
[arc(f,c)],
[arc(f,e)],
[arc(f,g)],
[arc(g,c)],
[arc(c,a)]).
edge(X,Y):-arc(X,Y);arc(Y,X).
path(X,Y):-edge(X,Y).
path(X,Y):-edge(X,Z),path(Z,Y).
triangle(X,Y,Z):-arc(X,Y),arc(Y,Z),arc(Z,X).
cycle(X):-arc(X,Y),path(Y,X).
connectivity([]):-forall(member(edge(X,Y)),path(X,Y)).
Check:
connectivity(graph).
upper I have arc(x,y) and I need check if every pair is connected.
Could u help me ?
Since you changed the question after I was almost done I will post what would solve the question before the change and you can figure out how to change it to meet your update.
arc(a,b).
arc(a,f).
arc(b,c).
arc(c,d).
arc(c,e).
arc(e,d).
arc(f,c).
arc(f,e).
arc(f,g).
arc(g,c).
arc(c,a).
edge(X,Y) :-
arc(X,Y), !.
edge(X,Y) :-
arc(Y,X).
path_prime(Visited,X,Y) :-
\+ member(X,Visited),
edge(X,Y), !.
path_prime(Visited,X,Y) :-
\+ member(X,Visited),
edge(X,Z),
path_prime([X|Visited],Z,Y).
path(X,X) :-
ground(X), !.
path(X,Y) :-
path_prime([],X,Y).
nodes(Nodes) :-
setof(A,B^arc(A,B),Starts),
setof(B,A^arc(A,B),Ends),
union(Starts,Ends,Nodes).
connected(X,Y) :-
nodes(Nodes),
member(X,Nodes),
member(Y,Nodes),
path(X,Y).
The first thing that has to be done is to get a list of the unique nodes which will be a set.
This can be done using
nodes(Nodes) :-
setof(A,B^arc(A,B),Starts),
setof(B,A^arc(A,B),Ends),
union(Starts,Ends,Nodes).
Notice that both the start and the end node of an arc are done separately. In particular notice that the node d is only in the destination of an arc.
Since you included edge(X,Y):-arc(X,Y);arc(Y,X). in your question, this indicated that the arcs should not be directional and so it is possible to get cycles. To avoid the cycles the list of visited nodes is added to the argument list and checked before proceeding.
As no test cases or examples of a correct solution were given, some times a node connected to itself is valid and so the clause
path(X,X) :-
ground(X), !.
was added.
This is by no means an optimal or best way to do this, just to give you something that works.
Partial run
?- connected(X,Y).
X = Y, Y = a ;
X = a,
Y = b ;
X = a,
Y = c ;
X = a,
Y = d ;
X = a,
Y = e ;
X = a,
Y = f ;
X = a,
Y = g ;
X = b,
Y = a ;
X = Y, Y = b ;
X = b,
Y = c ;
...
As I often comment, you should do problems with pen an paper first before writing code. If you don't know exactly what the code will be before you start typing the first line of code then why are you typing in code?
Questions from comments:
And setof ,union ,whats mean? Im rly beigneer and I don't understand that language and predicates.
setof/3 collects all of the values from arc/2. Since only one of the two values is needed, ^ tells setup/3 not to bind the variable in the Goal, or in beginner terms to just ignore the values from the variable.
union/3 just combines the to sets into one set; remember that a set will only have unique values.
Program should check if there is a direct route between two given cities. Alternatively, it can list all the connected cities for a given city.
My Solution is:
I keep a list of cities I visit. If the next city is not the city I came from and the name of the next city is not on the list, I let you press the screen.
My code is:
% knowledge base
path(newyork,losangeles).
path(losangeles,newyork).
path(losangeles,chicago).
path(chicago,losangeles).
path(chicago,houston).
path(houston,chicago).
path(houston,newyork).
path(newyork,houston).
% rules
route(X,Y):-myroute(X,X,Y,[]).
myroute(X,Y,Z,_L):- path(Y,Z), not(X = Z).
myroute(X,Y,A,L):- path(Y,A), not(X = A) , not(member(A,L)),
append(X,L,T) , myroute(Y,A,_Q,T).
Output:
?- route(newyork,Y).
Y = losangeles ;
Y = houston ;
false
Expected Output:
?- route(newyork,Y).
Y = losangeles ;
Y = chicago ;
Y = houston ;
false
My code is check if there is a direct route between two given cities. It can't list all the connected cities for a given city. Where am I making mistakes?
Let's follow the logic of your code:
?- route(newyork, Y).
Prolog tries to unify this with what it knows from the database. It finds this definition:
route(X,Y):-myroute(X,X,Y,[]).
Therefore it unifies X with newyork and proceeds to check if
myroute(newyork, newyork, Y, []) is true, as per the definition of route.
Prolog finds a match for that term in it database, with the first definition of the myroute predicate, and proceeds to unify X, Y and _L in that definition as newyork, newyork and [], i.e. it checks to see if myroute(newyork, newyork, Y, []) can be satisfied given the definition for myroute.
According to the first definition of myroute, prolog checks to see if it can find a fact in its database that will satisfy path(newyork, Z), such that Z is not newyork. There are two facts that can satisfy this, for Z=losangeles and for Z=houston. Note that there is no path(newyork, chicago) fact in the database, therefore this does not qualify as an answer given your current definitions.
Note also that if the first myroute definition fails, then so will the second one, since it checks for exactly the same things first! Therefore there will be no other solutions found via backtracking in this instance.
What you should be doing instead
I won't solve your exercise for you (especially since you didn't ask me to :p ) but in general, your myroute predicate is probably expected to work as follows:
myroute(StartPoint, EndPoint, RouteSoFar) :- % your definition here
and called from route like so:
route(X, Y) :- myroute(X, Y, []).
RouteSoFar is an accumulator; in the first call we call it with [] because we haven't visited any nodes, but in subsequent calls to myroute, it will be a list that you add to before calling the predicate again recursively. Somewhere in your predicate definition you'll have to check that the node you're about to visit isn't in the list of visited nodes already.
The logic of myroute is this: "There exists a myroute from X to Z if there is a simple path from X to Z (base case), OR if there is a path from X to an intermediate UNVISITED node Y, and then a myroute from that Y to Z (recursive case)".
I have the following facts that build ne of the wings of my dungeon.
path(room1,e,room2).
path(room2,w,room1).
path(room2,e,room5).
path(room5,w,room2).
path(room5,e,room6).
path(room6,w,room5).
path(room2,n,room3).
path(room3,s,room2).
path(room3,e,room4).
path(room4,w,room3).
path(room4,s,room5).
path(room5,n,room4).
path(room5,s,room7).
path(room7,n,room5).
path(room7,e,room8) :- at(key, in_hand).
path(room7,e,room8) :- write('The door appears to be locked.'), nl, fail.
path(room8,w,room7).
path(room2,s,room9).
path(room9,n,room2).
path(room9,s,room10).
path(room10,n,room9).
path(room10,w,room11).
path(room11,e,room10).
path(room11,s,room12).
path(room12,n,room11).
now i want to check if i one room is connected to another and then perhaps display one of the possible paths.
i tried to do something like this
connected(X,Y):-path(X,_,Z),path(Z,_,Y).
but it keeps giving me false, what am i doing wrong.
Try something like
connect(Bgn,End,Path) :- % to find a path between two nodes in the graph
connected(Bgn,End,[],P) , % - we invoke the helper, seeding the visited list as the empty list
reverse(P,Path) % - on success, since the path built has stack semantics, we need to reverse it, unifying it with the result
.
connected(X,Y,V,[X,Y|V]) :- % can we get directly from X to Y?
path(X,_,Y) % - if so, we succeed, marking X and Y as visited
. %
connected(X,Y,V,P) :- % otherwise...
path(X,_,T) , % - if a path exists from X to another room (T)
T \= Y , % - such that T is not Y
\+ member(X,V) , % - and we have not yet visited X
connected(T,Y,[X|V],P) % - we continue on, marking X as visited.
.
You'll note the test for having visited the room before. If your graph has cycles, if you don't have some sort of test for having previously visited the node, you'll wind up going around and around in circles...until you get a stack overflow.