Prolog pathfinder - prolog

I'm interested in pathfinder stuff and found a cool example on a site from 2011 but there isn't any explanation for the code. I understand what it does but don't understand how the steps work. So, for example these are the edges:
edge(1,2).
edge(1,4).
edge(2,4).
edge(3,6).
edge(3,7).
edge(4,3).
edge(4,5).
edge(5,6).
edge(5,7).
edge(6,5).
edge(7,5).
edge(8,6).
edge(8,7).
and with this I can tell if there is path between them:
path(X,Y,[X,Y]):- edge(X,Y).
path(X,Y,[X|Xs]):- edge(X,W), path(W,Y,Xs).
The output is something like this:
path(1,6,Xs).
Xs = [1,2,4,5,6];
Xs = [1,4,5,6];
...
But how does it exactly work?
What does [X,Y] do in the first line and what happens in the second?

The crucial thing to understand in this example is how recursive predicates work. First of all, recursion always needs a recursion step (recursive use of the current predicate), and a recursion anchor (the step where the recursion stops). The resolution algorithm is a depth-first search, and whereever there are multiple options to choose from (i.e., a ; or different rules or facts with the signature), the interpreter chooses from top to bottom and from left to right. To avoid infinite evaluations, the recursion anchor needs to be on the top like it is here, and the recursion step should be on the right of the second rule.
In the above example, the recursion stops when there is a direct edge between Xand Y, because that's where the path ends. Keep in mind that the rules are implications from right to left. As the third parameter is an output argument (the result you want to get), it needs to be initialized first in the anchor. [X,Y] does that by starting it with a list that contains the last two elements of the path. The rule is equivalent to the following:
path(X,Y,Result):- edge(X,Y), Result = [X,Y].
The second rule aims to find intermediate path elements: It assumes there is an edge(X,W) to an intermediate element W, and then a path from W to Y. The interpreter will try every edge from X to possible Ws. If there exists a path from a W to Y, there also is a path from X to Y, and the second rule becomes true for that step. The result of the recursive use to the predicate (the path list in the third parameter) will be Xs. So all that needs to be done in the current step is to add the X to the result list ([X|Xs]). Again, that is equivalent to:
path(X,Y,Result):- edge(X,W), path(W,Y,Xs), Result=[X|Xs].
Long story short: The resulting list is started with the last two elements in the recursion anchor, which then gets passed backwards through all recursive steps, and each step add its current X to the front to the list.
Of course recursion can still be infinite when there are cycles in the data (and paths) like in the example. If you want to avoid such cycles (and likely unwanted solutions such as paths where elements appear multiple times), you can keep track of the elements already visited:
path(X,Y,[X,Y],V):- \+member(X,V),\+member(Y,V),edge(X,Y).
path(X,Y,[X|Xs],V):- \+member(X,V),edge(X,W), path(W,Y,Xs,[X|V]).
In this solution, the list in the additional forth parameter collects the items already visited in an additional list. With \+member(X,V) it can be checked if the current X is already contained in V. There are other ways this can be implemented, for example by just using V as a result an reverting it in the anchor. V needs to be initialized in the query with an empty list:
?- path(1,6,R,[]).
R = [1, 2, 4, 3, 6] ;
R = [1, 2, 4, 3, 7, 5, 6] ;
...

Related

Prolog recursion doesn't work the other way

I am learning Prolog and I got stuck in the code where the knowledge base and base rules are:
rectangle(a1, 3, 5). %Name,Width,Height
rectangle(a2, 1, 2).
rectangle(a3, 4, 3).
calcWH(T,C) :-
rectangle(T,W,_), C is W. % finds the width of the rect.
The recursive part is:
calcWTH([],0). % Base case
calcWTH([H | T ] ,K) :-
length([H|T],L),
L =< 3,
calcWTH(T,M),
calcWH(H,O),
K is O+M.
What I want to do with this code is, with calcWTH I want to calculate total width of the list of rectangles. For example
calcWTH([a1,a2,a3],A)
returns 8, as expected. The thing I'm puzzled is that this code only works for one way, not the other. For example when I make a query like
calcWTH(A,3)
It first finds A= [a1], then A=[a2,a2,a2], and afterwards I expect the program to stop because of the part length([H|T],L), L =< 3 But It gets into a loop indefinitely. What am I missing here?
Think about what this is doing:
length([H|T],L), L =< 3
Make a list
Check its length is less than 3, if it is - then success, other wise back track to length/2 and try making another list and then check if that list is length is less than 3.
But we know that list/2 is creating lists in order of larger size,
so we know that if list of length 3 fails, then the next list
created on back tracking will be bigger and all other lists.. but
prolog does not know that. You could imagine writing a length
predicate that searchs for lists in a differnt order, maybe
randomly, or from a predefined big size to ever smaller lists.
Then we would want the backtracking to work as is.
Anyway that is why you get infinite recursion, it will keep trying new lists, of ever increasing size.
You might find adding constraints is a good way to solve your problem:
Using a constrained variable with `length/2`

Prolog get the number of nested list + 1 where an element is into a list

I'd like to return the depth level or number of nested list where certain element is. Also as condition the list doesn't have repeated elements. I am trying to understand this solution where I have two main doubts:
profundidad([],_,0):-!.
profundidad([A],A,1):-!.
profundidad([A|_],A,1):-!.
profundidad([H|_],A,N):-
profundidad(H,A,R),N is R+1.
profundidad([_|X],A,N):-
profundidad(X,A,N),!.
The correct output would be:
profundidad([2,3,4,5,[[6]],10],6,X).
X = 3
First, why we do put the cut operator ! from 1-3 statements? I know it prevents compiler from considering later statements when a solution is found.
Second, how we could read 4th and 5th cases in natural language?
The depth of an element A when the list is splitted by the head H and the rest _, is equal to the number R of steps plus 1.
profundidad([H|_],A,N):-
profundidad(H,A,R),N is R+1.
And those two sentences I think they are the same as previous ones but to go forward into the list:
profundidad([_|X],A,N):-
profundidad(X,A,N),!.
Plus, I am doubting now about why to not put [] into recursive called to:
profundidad(X,A,N),!.
I think it is to go deep into the nested lists but I am not sure.
Thank you.
It's better to avoid cuts when possible, in the following rewrite and simplification the test H\=A make the three clauses disjunctive.
profundidad([A|_],A,1).
profundidad([H|_],A,N):-
% H\=A,
profundidad(H,A,R),
N is R+1.
profundidad([H|T],A,N):-
H\=A,
profundidad(T,A,N).
The second clause doesn't need the test, since it goes down a level in the list, meaning it will succeed when it's a list, and then cannot unify with the target element anyway. For clarity it could stay there - it doesn't harm.
If your Prolog has dif/2, you could use that instead of (\=)/2, depending on your requirements about the generality (WRT variables instantiation) of the solution.

Out of local stack error being thrown in list of lists

My input: A list of lists with 0s and 1s. 0s being walkable blocks and 1s being walls. My starting point (1,1) and my ending point (for now we will use (2,2)).
My output: The pathway taken to get from 1,1 to 2,2.
My issue: I am accessing something out of index and I do not understand why.
Here is my code:
maze([[0, 1, 0, 0],
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 1],
[0, 0, 0, 0]]).
checkfor(X,Y,Maze) :-
nth1(Y, Maze, R),
nth1(X, R, 0),
assertz(w(X,Y)).
findadjacentsquare(OldX,OldY,X,Y,Maze) :-
nextmove(OldX,OldY,X,Y),
checkfor(X,Y,Maze).
nextmove(OldX,OldY,OldX,Y) :-
Y is OldY+1.
%Y >= 0,
%Y < 5.
nextmove(OldX,OldY,X,OldY) :-
X is OldX+1.
%X >= 0,
%X < 5.
nextmove(OldX,OldY,OldX,Y) :-
Y is OldY-1.
%Y >= 0,
%Y < 5.
nextmove(OldX,OldY,X,OldY) :-
X is OldX-1.
%X >= 0,
%X < 5.
citypath(X,Y,X,Y,Maze,Path) :-
write(Path).
citypath(OldX,OldY,X,Y,Maze,Path) :-
findadjacentsquare(OldX,OldY,X1,Y1,Maze),
citypath(X1,Y1,X,Y,Maze,[w(X1,Y1)|Path]).
So when calling it here is what I'm doing:
maze(M1), citypath(1,1,2,2,M1,Path)
Now I don't have any of the logic in here to save the path yet, but that isn't the issue. My issue is when I run this it is getting the out of local stack error and I can't seem to figure out why. I tried to hard-code constraints in the nextmove statements but that doesn't seem to be working either.
In your definition, better remove the assertz/1. And add using path/4.
nextstep(Maze,S0,S1) :-
S0 = X0-Y0,
S1 = X1-Y1,
findadjacentsquare(X0,Y0,X1,Y1,Maze).
mazepath(Path) :-
maze(Maze),
path(nextstep(Maze), Path, 1-1,2-2).
?- mazepath(Path).
Path = [1-1,1-2,1-3,1-4,1-5,2-5,3-5,3-4,3-3,4-3,4-2,4-1,3-1,3-2,2-2]
; Path = [1-1,1-2,1-3,1-4,1-5,2-5,3-5,3-4,3-3,4-3,4-2,3-2,2-2]
; Path = [1-1,1-2,1-3,1-4,1-5,2-5,3-5,3-4,3-3,3-2,2-2]
; Path = [1-1,1-2,2-2]
; false.
So the idea here is to reuse a generic predicate for path finding. That handles all the details of loop-checking.
Since you tried to use assert: This is quite possible too, but it requires a lot of additional overhead. For example, you need to initialize the state. You need to make sure that the code remains re-entrant such that multiple searches are possible simultaneously. And so on ... Brief, keeping your code clean is often a preferable approach.
"Now I don't have any of the logic in here to save the path yet, but that isn't the issue"
No, that is the issue.
Specifically, the reason you're getting into an out of stack error is because the logic of the code forces you to keep going back and forth between positions (3,5) and (4,5) indefinitely.
When you're at (3,5) the first nextmove predicate fails, and the second one succeeds, so you set x=4. The move succeeds and you proceed to calculate the next move starting from (4,5).
When you're at (4,5), the first three nextmove predicates fail, and the fourth one succeeds, so you set x=3. The move succeeds and you proceed to calculate the next move starting from (3,5)
And so on forever.
The way to avoid this is to also check your path so far, in your checkfor predicate, and make your current move fail if this path has been encountered before. (assuming that the rules of the game forbid you from visiting the same position twice and you're trying to find an acyclic route instead -- note that this is simply acyclic, and not necessarily the shortest one).
In other words:
First time you're at (3,5) position, the first nextmove predicate fails, but the second provisionally "succeeds", so you move to (4,5).
From (4,5) the first three nextmove predicates fail, but the fourth succeeds, attempting to push you back to (3,5). However, checkfor detects this is in the path already, and fails. Because there are no more nextmove predicates to try, the whole findadjacentsquare predicate fails.
This causes you to backtrack all the way to where it all went wrong, i.e. the second nextmove predicate of move (3,5), which as it turns out didn't succeed down the line after all. So prolog will now proceed to try the third nextmove predicate. Your pawn will now move up into (3,4) instead, the move succeeds, and you continue from there.
etc.
Other miscellaneous advice:
Try xpce with the guitracer; makes the whole debugging process very easy to visualise!
Your assert statement is useless here and does nothing: there's no reason to modify your database by asserting new facts, since you're not independently checking against those facts anywhere later on in your code. Instead you're appending the unified waypoints to a list in an accumulator pattern (which is exactly what you should be doing, but has nothing to do with checking against existing / asserted facts in your database).
At the point of your query, it doesn't make sense to pass an uninitialised Path argument, since your 'base case' predicate prints the accumulated list which is a bunch of waypoints appended to your initial input, therefore your initial input needs to be something specific. You should pass an empty list, or at least the first point, i.e. w(1,1).

Recursively finding sum in prolog

I'm trying to use Prolog to find a sum of a path using recursion. I need to pass a list of nodes of a graph, then have it return the sum of their weights.
This is what I've tried but I'm not sure if I'm on the right track.
connect(a,b,5).
connect(b,c,8).
connect(a,d,10).
connect(d,e,6).
connect(d,f,11).
connect(d,g,4).
connect(b,d,2).
connect(b,e,9).
connect(c,d,4).
connect(c,f,5).
connect(e,g,2).
connect(f,g,1).
list_sum([], 0).
list_sum([Head | Tail], TotalSum) :-
list_sum(connect(Head,Tail,X), Sum1),
TotalSum is Head + Sum1.
Example goal:
list_sum([a,b,c],Sum).
Sum = 13
I see three problems with your code. The first is that you have a logic variable X that you are not using, the second is that your predicate list_sum takes a list as its first element and yet you are giving it a predicate connect(Head,Tail,X), the third is that you are using Head in an addition whereas apparently Head is an atom, not an Integer (maybe you meant X here), the fourth (I'm finding them as I go) is that the second argument of the predicate connect is an atom (representing a node, in this case) and you are giving it a list.
And a fifth problem with your question: you seem to think that the weights are on the nodes where they are clearly on the edges.
So I think the question of your assignment is two-folds:
Check that the path given to you is actually a path (in that there is a connection between each element and the next)
If it is indeed a path, sum the weights of your connections along the way.
In Prolog, the core artifact of programming are predicates, not functions. So to get the weight of a given link, you call connect(Head,NextNode, Weight), which gives you (through unification) both the Weight and a possible NextNode, then the recursive call will check if that NextNode is indeed the next element in the list. After the recursive call, you use Weight instead of Head, and it should be a bit closer to the solution.
PS: Feel free to create a list_sum_aux/3 and use it instead.

List Recursive Predicates, advanced

Just going through some tutorials and I found some advanced recursive procedurs like flatten. I have tried to google to find similar examples that involve multiple recursions (head tail) but could not get the result I required.
Could you infer some predicates or tutorials that cover advanced list recusions (on both head, tail)?
Just to expand a bit on what #hardmath is saying, let's look at the definition of lists:
Base case: []
Inductive case: [Head|Tail]
What makes this a recursive data structure is that Tail is also a list. So when you see [1,2,3], you're also seeing [1|[2|[3|[]]]]. Let's prove it:
?- X = [1|[2|[3|[]]]].
X = [1, 2, 3].
So more "advanced" forms of recursion are forms that either involve more complex recursive data types or more complex computations. The next recursive data type most people are exposed to are binary trees, and binary trees have the nice property that they have two branches per node, so let's look at trees for a second.
First we need a nice definition like the definition from lists. I propose the following:
Base case: empty
Inductive case: tree(LeftBranch, Value, RightBranch)
Now let's create some example trees just to get a feel for how they look:
% this is like the empty list: no data
empty
% this is your basic tree of one node
tree(empty, 1, empty)
% this is a tree with two nodes
tree(tree(empty, 1, empty), 2, empty).
Structurally, the last example there would probably look something like this:
2
/
1
Now let's make a fuller example with several levels. Let's build this tree:
10
/ \
5 9
/ \ / \
4 6 7 14
In our Prolog syntax it's going to look like this:
tree(tree(tree(empty, 4, empty), 5, tree(empty, 6, empty)),
10,
tree(tree(empty, 7, empty), 9, tree(empty, 14, empty)))
The first thing we're going to want is a way to add up the size of the tree. Like with lists, we need to consider our base case and then our inductive cases.
% base case
tree_size(empty, 0).
% inductive case
tree_size(tree(Left, _, Right), Size) :-
tree_size(Left, LeftSize),
tree_size(Right, RightSize),
Size is LeftSize + RightSize + 1.
For comparison, let's look at list length:
% base case
length([], 0).
% inductive case
length([_|Rest], Length) :-
length(Rest, LengthOfRest),
Length is LengthOfRest + 1.
Edit: #false points out that though the above is intuitive, a version with better logical properties can be produced by changing the inductive case to:
length([_|Rest], Length) :-
length(Rest, LengthOfRest),
succ(LengthOfRest, Length).
So you can see the hallmarks of recursively processing data structures clearly by comparing these two:
You are given a recursive data structure, defined in terms of base cases and inductive cases.
You write the base of your rule to handle the base case.
This step is usually obvious; in the case of length or size, your data structure will have a base case that is empty so you just have to associate zero with that case.
You write the inductive step of your rule.
The inductive step takes the recursive case of the data structure and handles whatever that case adds, and combining that with the result of recursively calling your rule to process "the rest" of the data structure.
Because lists are only recursive in one direction there's only one recursive call in most list processing rules. Because trees have two branches there can be one or two depending on whether you need to process the whole tree or just go down one path. Both lists and trees effectively have two "constructors," so most rules will have two bodies, one to handle the empty case and one to handle the inductive case. More complex structures, such as language grammars, can have more than two basic patterns, and usually you'll either process all of them separately or you'll just be seeking out one pattern in particular.
As an exercise, you may want to try writing search, insert, height, balance or is_balanced and various other tree queries to get more familiar with the process.

Resources