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) .
Related
I am new to Prolog and I'm trying to write a predicate that takes two nodes and a graph as it's arguments and then checks whether there exists a direct path between these two nodes in the graph.
For example, there is a direct path from n(1) to n(4) in the graph below, that goes from n(1)
to n(3) and from n(3) to n(4):
g[n(1), n(4), g([n(1), n(2), n(3), n(4), n(5), n(6), n(7), n(8)],
[e(n(1), n(2)), e(n(1), n(3)), e(n(3), n(4)), e(n(4), n(5)), e(n(5), n(6)), e(n(5), n(7)), e(n(7), n(8))]))
In the end
?− dirPath(n(1), n(4), g[n(1), n(4), g([n(1), n(2), n(3), n(4), n(5), n(6), n(7), n(8)],
[e(n(1), n(2)), e(n(1), n(3)), e(n(3), n(4)), e(n(4), n(5)), e(n(5), n(6)), e(n(5), n(7)), e(n(7), n(8))]))
should return true.
My tentative code looks like this:
edge(n(1),n(2)).
edge(n(1),n(3)).
edge(n(3),n(4)).
edge(n(4),n(5)).
edge(n(5),n(6)).
edge(n(5),n(7)).
edge(n(7),n(8)).
dirPath(A,B, g) :- % - graph argument still missing.
move(A,B,[]) % - two nodes are connected,
. % - if one can move from one to the other,
move(A,B,V) :- % - and one can move from A to B
edge(A,X) , % - if A is connected to X, and
not(member(X,V)) , % - one hasn't yet reached X, and
( % - either
B = X % - X is the desired destination
; % OR
move(X,B,[A|V]) % - one can get to that destination from X
)
.
My main problem is that I can't figure out how make my dirPath predicate accepts a graph, as it's argument.
First of all, this:
g[n(1), n(4), g([n(1), n(2), n(3), n(4), n(5), n(6), n(7), n(8)],
[e(n(1), n(2)), e(n(1), n(3)), e(n(3), n(4)), e(n(4), n(5)), e(n(5), n(6)), e(n(5), n(7)), e(n(7), n(8))]))
is not valid syntax. A term cannot begin g[.... This should probably be g([... instead.
Second, the representation is not entirely intuitive. What are the special roles of n(1) and n(4)? Why is there an inner g(...) term?
Nevertheless, it's clear which part is supposed to be the edges. Let's define a predicate to access just the edges:
graph_edges(Graph, Edges) :-
Graph = g(_Something, _SomethingElse, g(_Nodes, Edges)).
Selection of one edge from the graph is then:
graph_edge(Graph, Edge) :-
graph_edges(Graph, Edges),
member(Edge, Edges).
You can then incorporate this into your predicate like this:
dirPath(A, B, Graph) :-
move(A, B, Graph, []).
move(A, B, Graph, Vs) :-
graph_edge(Graph, e(A, X)),
...
Note that I changed your variable V to Vs. Single-letter variable names are often not great in general (From and To would in my opinion be clearer than A and B). Single-letter variable names for lists are especially unusual; the common convention is to add s (as in the English plural, visited nodes) for lists. For graphs this is especially recommended, since a simple V could very well be interpreted as a single "vertex".
(Solution not tested.)
I am new to logic programming. I am trying to write a program which can find all nodes which can reach to an specific node.
Here is my code:
link(0, 1).
link(3, 4).
link(1, 2).
link(2, 0).
link(2, 1).
link(3, 2).
link(4, 3).
So show above by graph, it should look like below:
I want to do something like this:
?- go(X,0).
X=1;
X=2;
X=3;
X=4;
....
Which mean that from all 1,2,3 and 4 can go to 0.
[1->2->0],[2->0],[3->2->0],[4->3->2->0]...
so I try
go(X,Y) :- link(X,Y).
go(X,Y) :- link(X,Z),go(Z,Y).
?- go(X,0).
X=2;
X=0;
X=0;
X=0;
X=0;
....
but i don't want 0 as one of the output, it is meaningless to show I can go to 0 when I am already in 0. Also, it keeps repeating.
I try to fix this problem by:
go(X,Y) :- link(X,Y).
go(X,Y) :- (X\==Y),link(X,Z),go(Z,Y).
?- go(X,0).
X = 2 ;
false.
I'm not sure why it will stop at X=2. I try to draw the ProLog search tree and i still don't know why my code wont continue go for other facts(link(3,4)). Seem it stop at some point and no keep going for the green part below:
I try to test it using go(1,0). to go(4,0).
go(1,0) and go(2,0) success
But go(3,0) and go(4,0) return Error: Stack limit. I found that the recursion keep going and going between node 3 and 4. But I don't really know how to solve it.
I am so confused now, please tell me what is my mistake, or how to implement this function in a correct way? Thank you.
The problem is that your graph is a cyclic, rather than acyclic graph. That means that starting from some node X, you [eventually] will return to X.
That means that (for starters) unless you handle the cycles somehow, you will be stuck in an endless recursive loop (until you run out of stack space).
As you traverse the graph, you need to maintain some extra state (a list of nodes that you have already visited). To do this in Prolog, it is a common idiom to use a helper predicate, with the same name but additional arguments that carry the extra state.
So, try something like this:
% the public predicate. We seed the visited list here
% with our starting node.
%
% Not to worry if it is an unbound
% variable. It will become bound/unbound as necessary.
traverse(A,B) :- traverse(A,B,[A]).
traverse(A,B,V) :- % We can get from A to B, if...
link(A,B), % - A and B are directly connected, and
\+ member(B,V) % - we haven't already visited B
. % - easy!
traverse(A,B,V) :- % Otherwise, we can get from A to B, if...
link(A,X), % - we can get to some intermediate node X,
\+ member(X,V) % - that we have not yet visited, and
traverse(X,B,[X|V]) % - we can get from X to B (adding X to the visited list
.
I have a data given in the form of an adjacency matrix signifying the edges, out of which I was able to create a edges.pl file containing all the edges between the cities. For reference see the image,
In the edges.pl file I have facts of the form edge(agartala, ahmedabad, 3305), edge(ahmedabad, agartala, 3305) to create a bidirectional graph.
There are 47 cities in total.
Now I want to create another file, where I want to create facts that give shortest distance between each pair of cities. That is, something like a 47x47 matrix where each city on row is a source and each city on column is a destination.
Basically, I want to implement loops so that I can run Dijkstra on each pair of cities and store dynamic facts like,
distance(city1, city2, shortest_distance).
I don't understand how to run these nested loops on these cities.
Since you want to find the shortest paths for all pairs of cities, isn't the Floyd-Warshall algorithm what you should be using instead of Dijkstra's? I will use Floyd-Warshall in this answer. What both algorithms have in common is that they need to make updates to some data structure. I will use the Prolog database for this, since you want to assert some dynamic facts anyway. However, the solution will be very "non-logical".
I will be using the following input graph from the Floyd-Warshall Wikipedia page:
node(1).
node(2).
node(3).
node(4).
edge(1, 3, -2).
edge(2, 1, 4).
edge(2, 3, 3).
edge(3, 4, 2).
edge(4, 2, -1).
As a commenter mentioned, a technique you can use here are failure-driven loops.
Here is what a failure-driven loop looks like:
visit_each_node :-
node(Node),
fail.
This doesn't seem to do anything:
?- visit_each_node.
false.
And indeed, logically it just fails. But procedurally it visits each node, it just doesn't do anything with it.
You can do things inside failure-driven loops, but only non-logical things like I/O, or modifications of the Prolog database. For example:
print_each_node :-
node(Node),
write('visiting '),
write(Node),
nl,
fail.
This will visit a node, print it, and come to the failure. The failure will force it to backtrack to find another node and print it. Then it will fail again, find another node, and so on, until all nodes have been visited, and finally the query fails:
?- print_each_node.
visiting 1
visiting 2
visiting 3
visiting 4
false.
We will usually want our loops to do something and then succeed. For this, we can just add another clause that succeeds:
print_each_node_and_succeed :-
node(Node),
write('visiting '),
write(Node),
nl,
fail.
print_each_node_and_succeed :-
% when we get here, all nodes have been visited
true.
This will do all of what I described above. Then, after the first clause has definitely failed, it will execute the second clause and will succeed:
?- print_each_node_and_succeed.
visiting 1
visiting 2
visiting 3
visiting 4
true.
In general, a failure-driven loop will consist of: (a) some goals that generate some data, (b) some goals that consume that data non-logically, (c) a fail, all in one clause, and (d) a second clause that succeeds:
generic_loop :-
% generate
some_x(X),
some_y(Y),
% consume
do_something_non_logical_with_x_and_y(X, Y),
% loop back
fail.
generic_loop :-
% nothing more to do
true.
Now, for the Floyd-Warshall algorithm. We'll populate the following dynamic predicate, using from_to_set_distance to encapsulate removing any old stored distance between two cities and storing a new one:
:- dynamic from_to_distance/3.
from_to_set_distance(From, To, Distance) :-
retractall(from_to_distance(From, To, _OldDistance)),
asserta(from_to_distance(From, To, Distance)).
The first step of the algorithm is to initialize all distances from given edges. This must visit all pairs of cities and do something non-logical, namely, update the Prolog database. A job for a failure-driven loop!
initialize_distances :-
retractall(from_to_distance(_From, _To, _Distance)),
node(U),
node(V),
( U = V
-> from_to_set_distance(U, U, 0)
; edge(U, V, Distance)
-> from_to_set_distance(U, V, Distance)
; Infinity = 999999,
from_to_set_distance(U, V, Infinity) ),
% failure-driven loop
fail.
initialize_distances :-
% when we get here, all pairs of nodes have been initialized
true.
The second step is to visit all triples of cities and do something non-logical: Update the stored distance if we found a shorter path than the one we had stored before. Again, a job for a failure-driven loop!
compute_pairwise_distances :-
node(K),
node(I),
node(J),
from_to_distance(I, J, DistIJ),
from_to_distance(I, K, DistIK),
from_to_distance(K, J, DistKJ),
( DistIJ > DistIK + DistKJ
-> DistIKJ is DistIK + DistKJ,
from_to_set_distance(I, J, DistIKJ)
; % nothing to do
true ),
% failure-driven loop
fail.
compute_pairwise_distances :-
% when we get here, all triples of nodes have been visited and all
% pairwise distances computed
true.
The entire Floyd-Warshall algorithm first does the initialization, then the computation of all pairwise distances:
floyd_warshall :-
initialize_distances,
compute_pairwise_distances.
When we run this, it will not produce anything visible:
?- floyd_warshall.
true.
But the database will have been updated. To see all the stored distances, we can visit all of them and do something non-logical, namely, print them. A job for... you guessed it: a failure-driven loop!
?- from_to_distance(From, To, Distance), write(from_to_distance(From, To, Distance)), nl, fail.
from_to_distance(3,2,1)
from_to_distance(3,1,5)
from_to_distance(1,2,-1)
from_to_distance(2,4,4)
from_to_distance(1,4,0)
from_to_distance(4,3,1)
from_to_distance(4,1,3)
from_to_distance(2,3,2)
from_to_distance(4,4,0)
from_to_distance(4,2,-1)
from_to_distance(3,4,2)
from_to_distance(3,3,0)
from_to_distance(2,2,0)
from_to_distance(2,1,4)
from_to_distance(1,3,-2)
from_to_distance(1,1,0)
false.
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.
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.