Finding the path length of an Acyclic Graph in Prolog - prolog

Okay, so I have the graph:
and I want to be able to create a rule to find all the paths from X to Y and their lengths (number of edges). For
example, another path from a to e exists via d, f, and g. Its length is 4.
So far my code looks like this:
edge(a,b).
edge(b,e).
edge(a,c).
edge(c,d).
edge(e,d).
edge(d,f).
edge(d,g).
path(X, Y):-
edge(X, Y).
path(X, Y):-
edge(X, Z),
path(Z, Y).
I am a bit unsure how I should approach this. I've entered a lot of rules in that don't work and I am now confused. So, I thought I would bring it back to the basics and see what you guys could come up with. I would like to know why you done what you done also if that's possible. Thank you in advance.

This situation has been asked several times already. Firstly, your edge/2 predicates are incomplete, missing edges like edge(c,d), edge(f,g), or edge(g,e).
Secondly, you need to store the list of already visited nodes to avoid creating loops.
Then, when visiting a new node, you must check that this new node is not yet visited, in the Path variable. However, Path is not yet instanciated. So you need a delayed predicate to check looping (all_dif/1). Here is a simplified version using the lazy implementation by https://stackoverflow.com/users/4609915/repeat.
go(X, Y) :-
path(X, Y, Path),
length(Path, N),
write(Path), write(' '), write(N), nl.
path(X, Y, [X, Y]):-
edge(X, Y).
path(X, Y, [X | Path]):-
all_dif(Path),
edge(X, Z),
path(Z, Y, Path).
%https://stackoverflow.com/questions/30328433/definition-of-a-path-trail-walk
%which uses a dynamic predicate for defining path
%Here is the lazy implementation of loop-checking
all_dif(Xs) :- % enforce pairwise term inequality
freeze(Xs, all_dif_aux(Xs,[])). % (may be delayed)
all_dif_aux([], _).
all_dif_aux([E|Es], Vs) :-
maplist(dif(E), Vs), % is never delayed
freeze(Es, all_dif_aux(Es,[E|Vs])). % (may be delayed)
Here are some executions with comments
?- go(a,e).
[a,b,e] 3 %%% three nodes: length=2
true ;
[a,c,d,f,g,e] 6
true ;
[a,c,f,g,e] 5
true ;
[a,d,f,g,e] 5
true ;
false. %%% no more solutions
Is this a reply to your question ?

Related

Prevent cycle in Depth first search using prolog

Is there any way to prevent cycle in this code.
move(a,b).
move(b,a).
move(a,c).
move(b,d).
move(b,e).
move(d,h).
move(d,i).
move(e,j).
move(e,k).
move(c,f).
move(c,g).
move(f,l).
move(f,m).
move(g,n).
move(g,o).
goal(n).
goSolveTheMaze(Start,Way) :-
dfs(Start, Way),!.
dfs(Goal, [Goal]) :-
goal(Goal),!.
dfs(Start, [Start|Way]) :-
move(Start, N),
dfs(N, Way).
so when move(a,b) move to (b,c) dont go back to (b,a),
when run goSolveTheMaze(a,path).
The output should be path=[a,c,g,n].
What if you add a third argument to dfs which is a list of where you've already visited? You could then use \+/1 and member/2 to avoid returning to places you've already been.
For example, if you use the following:
move(a,b).
move(b,a).
move(a,c).
move(b,d).
move(b,e).
move(d,h).
move(d,i).
move(e,j).
move(e,k).
move(c,f).
move(c,g).
move(f,l).
move(f,m).
move(g,n).
move(g,o).
goal(n).
goSolveTheMaze(Start,Way) :-
dfs(Start, Way, [Start]),!.
dfs(Goal, [Goal], _) :-
goal(Goal),!.
dfs(Start, [Start|Way], Visited) :-
move(Start, N),
\+ member(N, Visited),
dfs(N, Way, [N|Visited]).
Then the query:
?- goSolveTheMaze(a, X).
Will produce the result:
X = [a, c, g, n]
Update in response to comment "can you tell me what \+ do?":
The \+ predicate is true when its argument cannot be proven. Therefore in the above example the line \+ member(N, Visited) means "when N is not a member of the list Visited".
See: https://www.swi-prolog.org/pldoc/man?predicate=%5C%2B/1

Write a predicate that works for cyclic graph to check if there exists a path between two nodes using prolog

I am trying to wrote a predicate isway(A, B) which should work for cyclic graphs. The expected outcome I am trying to get is, if there a path exists between 2 nodes or not. example: if I ask isway(a, f), expect it to answer either yes or no. below is my code but it never works and always gives me false as an output.
%given facts
edge(a,b).
edge(a,c).
edge(b,c).
edge(c,d).
edge(c,e).
edge(d,e).
edge(f,g).
edge(g,h).
edge2(d,a).
edge2(h,f).
edge2(X,Y) :- edge(X,Y).
%my implementation
isway(A,B) :- edge2(A,B,[]).
edge2(A,B,V) :- edge(A,X), not(member(X,V)), (B = X ; edge2(X,B,[A|V])).
%my output
?- isway(b,a). %I expect true for this one
false
?- isway(a,f). %False for this is ok but I am not confident about my logic
false.
You can use tabling for computing the transitive closure of the edge relation:
edge(a,b).
edge(a,c).
edge(b,c).
edge(c,d).
edge(c,e).
edge(d,e).
edge(f,g).
edge(g,h).
edge(d,a). % acyclic
edge(h,f). % acyclic
:- table path/2.
path(X, Y) :- edge(X, Y).
path(X, Y) :- path(X, Z), path(Z, Y).
Then, the queries work as you expect:
?- path(a, f).
false.
?- path(b, a).
true.

Find routes in graph

I'm trying to find all possible routes and then print to the user, but since I'm kind new to prolog, I'm having some trouble.
I have to print all possible ways to go to G from A. This is what I have so far:
direct_path(pathA,pathB).
direct_path(pathA,pathC).
direct_path(pathB,pathD).
direct_path(pathC,pathE).
direct_path(pathC,pathF).
direct_path(pathE,pathD).
direct_path(pathE,pathG).
direct_path(pathF,pathG).
upward(X, Y):- direct_path(X, Y).
upward(X, Z):- direct_path(X, Y), upward(Y, Z).
This way I can check if it's possible to get from A to G, but how to I do to print this paths?
Just add a third parameter to your upward predicate to store the path:
upward(X, Y, [X, Y]):- direct_path(X, Y).
upward(X, Z, [X | Rest]):- direct_path(X, Y), upward(Y, Z, Rest).
Test run:
?- upward(pathA, pathG, Path).
Path = [pathA, pathC, pathE, pathG] ;
Path = [pathA, pathC, pathF, pathG] ;
false.
So there are two different paths from A to G.

Existence Exception in Prolog Program

I'm trying to write a rule in my prolog program that determines if someone is the brother of someone else.
For example, if I type brother_of(chris, X) it will return christy because chris is the brother of christy. However, when I type this I get an existence exception. I've included facts to cover everything, but maybe it's a problem in my rule definition? The code is below.
/* Facts */
female(ella).
female(jodi).
female(sonya).
female(jane).
female(christy).
female(mary).
male(arnold).
male(chris).
male(louis).
male(mark).
father(arnold).
father(louis).
father(mark).
mother(ella).
mother(jodi).
mother(jane).
mother(mary).
father_of(arnold, chris). /* arnold is the father of chris */
father_of(arnold, christy).
father_of(louis, mark).
father_of(mark, arnold).
mother_of(mary, chris).
mother_of(mary, christy).
mother_of(jane, arnold).
mother_of(ella, sonya).
mother_of(jodi, ella).
mother_of(jodi, mark).
/* Rules */
brother_of(X, Y) :-
male(X),
((father_of(Z, X), father_of(Z, Y));
(mother_of(W, X), mother_of(W, Y))),
X =\= Y.
The operator =\= is used in arithmetic only (AFAIK), to see if two terms are different (non-unifiable) use the operator \=:
X \= Y.
Update: a brief introduction to the cut (!) goal: when the cut is used in a predicate, it means no more answers should be searched besides the already found ones (i.e. you are "cutting the remaining branches" in the search tree). Example:
first_child(X,Y) :-
father_of(X,Y), !.
?- first_child(arnold,Y).
Y = chris ;
no
After the cut is reached, all choice points before the cut are discarded (but new ones can be created after it). In you example, you know that if X and Y have the same father, it's irrelevant if they also have the same mother. So you can place the cut right after the "common father" part succeeds:
brother_of(X, Y) :-
male(X),
((father_of(Z, X), father_of(Z, Y), X \= Y, !); # If cut is reached, will not run the next line
(mother_of(W, X), mother_of(W, Y), X \= Y)).
Note however that using cut has many pitfalls (refer to "green cut" vs "red cut" in the linked Wikipedia article), but it's too much to describe here. (note how I repeated X \= Y - if I didn't do that the program would fail sometimes)
Lastly, I'd also like to point out that using ; is often discouraged when writing Prolog code (you can use when needed though). Instead, write it in two clauses:
brother_of(X, Y) :-
male(X),
father_of(Z, X),
father_of(Z, Y),
X \= Y,
!.
brother_of(X, Y) :-
male(X),
mother_of(W, X),
mother_of(W, Y),
X \= Y.
(this ; vs two clauses is a bit subjective, though, so I won't argue too much about it... just know that both ways are possible, and will produce the same result)

graph path in prolog

I've made the rules to obtain a path of a graph with the edges defined as follows:
graph(a,b).
graph(a,a).
graph(b,c).
but now I need to do it when the facts being, for example:
graph(a,[a,b,e]).
graph(b,[c,d]).
graph(d,[]).
I had this:
path(Origin, Dest, List) :- path(Origin, Dest, List, [Origin]).
path(X, X, List, Temp) :- reverse(Temp, List).
path(Origin, Dest, List, Temp) :- graph(Origin, Inter),
not(member(Inter, Temp)),
path(Inter, Dest, List, [Inter|Temp]).
and I know that problem is in graph(Origin, Inter), but I don't know how to tweak it so it can go inside the list, I've tried graph(Origin, [Inter|_]) but obviously it just checks the first one. Any help (even if it's not code) would be greatly appreciated.
Assuming directed graphs:
edge(X,Y) :- graph(X,Nodes), member(Y,Nodes).
I managed to solve it, in case someone else has the same problem:
node(X) :- graph(X,_).
allnodes(Nodes) :- setof(X, node(X), Nodes).
path(Origin,Dest,List) :- allnodes(X),
path(Origin, Dest, List1, X, X),
reverse(List1,List),!.
path(X,X,[X],_,_).
path(Origin,Dest,[Dest|List],[X|_],AN) :- graph(X, Inter),
member(Dest, Inter),
path(Origin,X,List,AN,AN).
path(Origin,Dest,List,[X|Y],AN) :- graph(X, Inter),
\+ member(Dest, Inter),
path(Origin,Dest,List,Y,AN).
Here's my solution that works on directed or undirected graphs, with or without cycles.
Using #larsmans suggestion you can make this work with adjacency-list like input.
edge(X,Y) :- graph(X,Nodes), member(Y,Nodes).
It also tries to find all paths, without revisiting.
c(1,2).
% ... c(X,Y) means X and Y are connected
d(X,Y):- c(X,Y).
d(X,Y):- c(Y,X).
% Use d instead of c to allow undirected graphs
findPathHelper(_, Begin, [], End):- d(Begin, End).
findPathHelper(Front, Begin, [Next|NMiddle], End):-
not(member(Begin,Front)),
d(Begin, Next),
append([Front,[Begin]], NFront),
findPathHelper(NFront, Next, NMiddle, End).
findPath(Start, End, Path):-
findPathHelper([], Start, Middle, End),
append([[Start],Middle,[End]], Path).

Resources