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).
Related
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
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 ?
I am writing a program in Prolog (gprolog) that pathfinds on a graph. I based it partially off of this SO post.
Let me give an example. Here is an example graph I drew:
And here is what the code initially looked like:
path([Goal],Goal,Goal).
path(P,Start,Goal) :- adjacent(Start,X),
\+ (memberchk(X,P)),
(
Goal=X
;
path([Start|P],X,Goal)
).
Regardless of whether that base case is redundant, here is the problem. I want an input style of
| ?- path(P,a,f).
and for that input, I would get output of
P = [a,s,f]
true ?
However, the problem with the code as it stands lies with memberchk. For memberchk(a,P), it attempt to unify, calls memberchk(a,[a|_]), and returns true. I don't want this to happen, so I first check if P is instantiated using the var/1 predicate. Thus, my code changed to
path([Goal],Goal,Goal).
path(P,Start,Goal) :- var(P),
path([],Start,Goal).
path(P,Start,Goal) :- adjacent(Start,X),
\+ (memberchk(X,P)),
(
Goal=X
;
path([Start|P],X,Goal)
).
Now if P is uninstantiated, we call path/3 with the empty list. My problem is this: now I cannot print P at the end, as I call path([],Start,Goal) and P is no longer associated with [].
I have tried using the write/1 predicate, but it either printed out P at every step or printed P = _26 (meaning it's printing the uninstantiated P, not the final value of P).
I hope this is a simple problem, I'm just awfully new to Prolog.
Apologies if something similar has been asked; I would love to be pointed to other questions that could help. I searched through SO and Google before posting this.
The concept you need is that of accumulators
You were actually very close: you realized indeed that initializing P to [], and filling it with [Start|P] as you recurse was a working strategy. This is called an accumulator, and to get the final result you simply need to add another argument.
Here is your new path/3 predicate that you query:
path(P, Start, Goal) :-
path([], P, Start, Goal).
As you can see, here we add the [] as a first argument to path/4, which we implement like this:
path(L, P, Goal, Goal) :-
reverse([Goal|L], P).
path(L, P, Start, Goal) :-
adjacent(Start, X),
\+ (memberchk(X, L)),
path([Start|L], P, X, Goal).
The first clause is here to terminate the recursion. Once the Start and Goal arguments are the same as you had noted, the recursion should be over. When using an accumulator this means that we unify the accumulator with the output argument. However, the accumulator contains the answer reversed (and lacks the final goal), so we have reverse([Goal|L], P).
The second clause is very similar to what you had written, with the exception that we now need to pass P as is to the recursive clause. Note that I have removed your disjunction in that clause, it isn't needed in that case.
The complete code:
path(P, Start, Goal) :-
path([], P, Start, Goal).
path(L, P, Goal, Goal) :-
reverse([Goal|L], P).
path(L, P, Start, Goal) :-
adjacent(Start, X),
\+ (memberchk(X, L)),
path([Start|L], P, X, Goal).
I solved my problem. The solution relies on:
Keeping track of visited nodes
When recursing, recursing on a smaller list
Checking if something is not a member of a list to prevent unification when not wanted
My code is as follows:
connected(X,Y) :- adjacent(X,Y);adjacent(Y,X).
not_member(_, []).
not_member(X, [Head|Tail]) :- X \== Head, not_member(X, Tail).
path(P,A,B):-path_helper(P,A,B,[Start]).
path_helper([X,Y],X,Y,_):-connected(X,Y).
path_helper([Goal],Goal,Goal,_).
path_helper([Start|[Head|P]],Start,Goal,Visited):-connected(Start,Head),not_member(Head,Visited),path_helper([Head|P],Head,Goal,[Head|Visited]).
I'm new to Prolog and currently implementing DFS (depth-first search) and BFS (breadth-first search) algorithms. My DFS works fine as the code below, but the BFS is terminated and aborted when it reaches the leaf node (it doesn't backtrack and continue searching).
I also read some sample code about this but there are some functions they don't define like s(Node, NewNode)... so it's hard to understand, or the version use Queue is too complicated.
Here is my code:
Some ground functions:
%connected(+Start, +Goal, -Weight)
connected(1,7,1).
connected(1,8,1).
connected(1,3,1).
connected(7,4,1).
connected(7,20,1).
connected(7,17,1).
connected(8,6,1).
connected(3,9,1).
connected(3,12,1).
connected(9,19,1).
connected(4,42,1).
connected(20,28,1).
connected(17,10,1).
connected2(X,Y,D) :- connected(X,Y,D).
connected2(X,Y,D) :- connected(Y,X,D).
next_node(Current, Next, Path) :-
connected2(Current, Next, _),
not(member(Next, Path)).
DFS implement:
depth_first(Goal, Goal, _, [Goal]).
depth_first(Start, Goal, Visited, [Start|Path]) :-
next_node(Start, Next_node, Visited),
write(Visited), nl,
depth_first(Next_node, Goal, [Next_node|Visited], Path).
BFS implement:
breadth_first(Goal, Goal, _,[Goal]).
breadth_first(Start, Goal, Visited, Path) :-
findall(X,
(connected2(X,Start,_),not(member(X,Visited))),
[T|Extend]),
write(Visited), nl,
append(Visited, [T|Extend], Visited2),
append(Path, [T|Extend], [Next|Path2]),
breadth_first(Next, Goal, Visited2, Path2).
The Path is something like the Queue list.
For example when call DFS:
?- depth_first(1, 28, [1], P).
BFS:
?- breadth_first(1, 28, [1], []).
First, the usual notion of s(A,B) is just like your connect2(A,B,_).
You should make your interface predicates explicit:
depth_first( Start, Goal, Path):-
depth_first( Start, Goal, [Start], Path).
Maintaining a queue in BFS is not complicated at all. Instead of Visited, have VisitedLists queue (pop from front; add at end; thus FIFO):
consed( A, B, [B|A]).
bfs( Goal, [Visited|Rest], Path) :- % take one from front
Visited = [Start|_],
Start \== Goal,
findall( X,
( connected2(X, Start, _), \+ member(X, Visited) ),
[T|Extend]),
maplist( consed(Visited), [T|Extend], VisitedExtended), % make many
append( Rest, VisitedExtended, UpdatedQueue), % put them at the end
bfs( Goal, UpdatedQueue, Path ).
When the goal is reached, Path is instantiated:
bfs(Goal, [[Goal|Visited]|_], Path):-
reverse([Goal|Visited], Path).
The interface call needs to be adjusted correspondingly. It should be
breadth_first( Start, Goal, Path):- bfs( Goal, [[Start]], Path).
later note: repeated appending of course causes quadratic slowdown, so for efficiency this should be re-written with difference lists (also a straightforward task).
domains
list=symbol*
predicates
path(symbol,symbol)
solve(symbol,symbol,list)
insert(symbol,list,list)
clauses
path(a,b).
path(b,c).
path(c,d).
path(d,e).
path(a,d).
path(c,e).
solve(X, Z, P):-
path(X,Z),
insert(Z,Temp,P),
P=Temp.
solve(X,Z,P):-
path(X,Y),
insert(Y,Temp,P),
P=Temp,
solve(Y,Z,P).
insert(X,[X|Tail],Tail).
insert(X,[Y|Tail],[Y|Tail1]):-
insert(X,Tail,Tail1).
Want to print the path which i have followed in going from one point to another.But getting errors.For examples, i want:
goal
solve(a,c,P).
Answer:P=[a,b,c]
I don't get what your solve predicate does. Here is my guess about how it should be :
solve(Start, End, [Start|PathMinusStart]) :-
solve_(Start, End, PathMinusStart).
solve_(Start, End, [End]) :-
path(Start, End).
solve_(Start, End, [Waypoint|Path]) :-
path(Start, Waypoint),
solve_(Waypoint, End, Path).
solve will give you the paths one after another using the ; to browse them (well, I guess at least since I don't know anything about the prolog implementation you're using).
search_path(Node, End, Temp, Path) :- path(Node,End),reverse([End | [Node | Temp]], Path).
search_path(Node, End, Temp, Path) :-
path(Node, Next),
not(member(Node, Temp)),
search_path(Next, End, [Node | Temp], Path).
solve(Start, End, Path) :- search_path(Start, End, [], Path).