Out of local stack when applying recursion - recursion

The context, first. What I am trying to modelate with prolog are two separated graphs, both represent a group of friends, so in both of them I can put the relation friend(X,Y), and, because it's doesn't have sense the friendship isn't mutual in this model, I also put the relation friend(Y, X).
So this means that both graphs have bidirectional relationships between their elements.
For example:
friend(foo1, foo2).
friend(foo2, foo1).
friend(foo3, foo4).
friend(foo4, foo3).
In which foo1 is related with foo2, and the same goes for foo3 and foo4, but the first two are not related with the another two ones.
Because it is a group of friends, it also doesn´t have sense that in the same group of friends, two people of the same group aren't friends, so I am using recursion to determine if one person is friend of another.
definitivefriend(X, Z) :- friend(X, Z).
definitivefriend(X, Z) :- friend(X, Y), definitivefriend(Y, Z).
The problem I have is when I try to check if one person of one group is friend of a person of the other group. In other words, check if one element of of a graph is related with another element of the other graph.
Instead of getting false, which is the expected result, the compiler (SWI-Prolog, in this case), gives me an error of out of local stack.
I want to know how to solve this.
Edit
So thanks to CapelliC I have an approach of this problem. Because the main objective is complete, but there's a secondary problem I will describe it from now on.
These are the two graphs I am working with. Remember that I said before, both graphs are biredirectional.
Here's my program in prolog:
writeit :- write('Frienship').
definitivefriend(X, Z) :- friend(X, Z), friend(Z, X).
definitivefriend(X, Y) :- friend(X, Z), X #< Z, definitivefriend(Z, Y), Y \= X.
friend(amanda, ryan). % graph1 %
friend(ryan, amanda).
friend(ryan, lisa).
friend(lisa, ryan).
friend(bryan, ryan).
friend(ryan, bryan).
friend(sara, ryan).
friend(ryan, sara).
friend(sara, simone).
friend(simone, sara). % graph2 %
friend(sandra, jeff).
friend(jeff, sandra).
friend(betty, jeff).
friend(jeff, betty).
friend(jeff, antonia).
friend(antonia, jeff).
friend(jeff, oskar).
friend(oskar, jeff).
friend(jeff, leslie).
friend(leslie, jeff).
And here is some of the outputs I got
?- definitivefriend(amanda, ryan).
true . % It's correct, both nodes are neighbours %
?- definitivefriend(amanda, simone).
true . % It's correct, both nodes are in the same graph %
?- definitivefriend(ryan, simone).
true . % It's correct, same explanation as before %
?- definitivefriend(simone, amanda).
false. % It's wrong, expected result is true %
?- definitivefriend(ryan, jeff).
false. % It's correct, nodes are in different graphs %
?- definitivefriend(amanda, leslie).
false. % It's correct, same explanation as before %
?- definitivefriend(sandra, oskar).
false. % It's wrong, expected result is true %
?- definitivefriend(oskar, sandra).
false. % It's wrong, expected result is true %
?- definitivefriend(betty, oskar).
true . % It's correct, both nodes are in the same graph %
?- definitivefriend(oskar, betty).
false. % It's wrong, expected result is true %
As I said in the comments, even with some elements of the same graph (excepting the neighbour ones), definitivefriend gives me false. And are some cases when I execute definitivefriend(X, Y) I get true, but when I execite definitivefriend(Y, X) I get false.

I feel that you are not modelling in the right way, anyway this seems working (abusing of the suggestion by Jean-Bernard, +1)
definitivefriend(X, Y) :-
friend(X, Y),
friend(Y, X).
definitivefriend(X, Y) :-
friend(X, Z), X #< Z,
definitivefriend(Z, Y), Y \= X.
edit: this cannot work with your model. I can't see any other way than following Daniel suggestion (+1).

For your second definitivefriend rule, add a condition that X < Y. This will avoid cycles. Then simply add a rule for:
definitivefriend(X,Y) :- definitivefriend(Y,X)
As it is now, you could have:
definitivefriend(1,2) :- friend(1,3), definitivefriend(3,2)
definitivefriend(3,2) :- friend(2,1), definitivefriend(1,2)
Which leads to infinite recursion

The problem, basically, is cycles. Your graph is acyclic, but your code is not. Here's the question. Suppose I give the query :- definitivefriend(foo1, foo2).. What's to stop Prolog from expanding that like this:
definitivefriend(foo1, foo2)
:- friend(foo1, foo2), definitivefriend(foo2, foo2). % by clause 2
:- friend(foo1, foo2), friend(foo2, foo1), definitivefriend(foo1, foo2). % by clause 2
:- friend(foo1, foo2), friend(foo2, foo1), friend(foo1, foo2),
definitivefriend(foo2, foo2). % by clause 2
etc.
#Jean-Bernard Pellerin provides one useful way to prevent cycles, by forcing a total ordering. I don't think that's the right approach here, but I can't quite put my finger on why. However, one thing you can do is provide a visited list to check against and not re-enter nodes you've already been to. That code's going to look like this:
definitivefriend(X, Z) :- definitivefriend(X, Z, [X]).
definitivefriend(X, Y, Visited) :-
friend(X, Y), \+ memberchk(Y, Visited).
definitivefriend(X, Z, Visited) :-
friend(X, Y), \+ memberchk(Y, Visited),
definitivefriend(Y, Z, [Y|Visited]).

Related

Finding the binary tree height in Prolog

So I have a task to create a binary tree using the following facts that define each node with its two child nodes:
node(root, a, b).
node(a, c, d).
node(b, z, y).
node(c, e, f).
node(d, x, w).
node(e, g, h).
node(z, v, u).
node(u, t, s).
node(t, r, q).
I want to define the rule height(RootNode, Height) to calculate the height of the binary tree that
starts at the RootNode. The height of a tree is the longest distance (number of nodes) from
the root node to the farthest away leaf node. A leaf node is a node that doesn’t have any
children. The height of a leaf node is 1.
The code I currently have is:
node(root, a, b).
node(a, c, d).
node(b, z, y).
node(c, e, f).
node(d, x, w).
node(e, g, h).
node(z, v, u).
node(u, t, s).
node(t, r, q).
height(nil, 0).
height(RootNode, Height):-
node(RootNode, Left, Right),
height(Left, LH),
height(Right, RH),
Height is max(LH, RH) + 1.
At the moment, I'm really stuck and would love to know what happens to this code when it runs, why I've done this incorrectly and where I should look to improve further? Appreciate any help.
The problem with your code is that your base case requires that each leaf node exists in the database in the form node(Leaf, nil, nil) but according to your example leaf nodes are just nodes that appear as the second or third argument of a node/3 fact but does not appear as the first argument of another node/3 fact.
So you may fix your code by changing your base case:
instead of
height(nil, 0).
use
height(Leaf, 1):-
\+node(Leaf, _, _).
(note that the base case has height 1 as per your description)
Test case:
?- height(root,A).
A = 6 ;
false.
You may also "join" both clauses in a single one:
height(RootNode, Height):-
node(RootNode, Left, Right) *->
(height(Left, LH),
height(Right, RH),
Height is max(LH, RH) + 1) ; Height = 1.
test run:
?- height(root,A).
A = 6.

Prolog recursively multiply

I'm trying to multiply two numbers in Prolog recursively i.e. 3*4 = 3+3+3+3 = 12.
My code is :
mult(0,Y,Y).
mult(X,Y,Z) :-
NewX is X-1,
Z is Y + mult(NewX,Y,Z).
but I keep either in an infinite loop or being told mult is not a function.
What you here constructed is a predicate. A predicate is not the same as a function in computer science, you can not write A is B + some_pred(C), or at least not as far as I know in ISO Prolog, and definitely not without adding some extra logic.
In order to pass values, one uses variables. We can thus call the mult/3 predicate recursively, and use a variable that will be unified with the result. We can then perform arithmetic with it, like:
mult(0, _, 0).
mult(X, Y, Z) :-
X1 is X - 1,
mult(X1, Y, Z1),
Z is Y + Z1.
Note that you can not reassign a (different) value to a variable. So if, like you did in the question, use Z twice, then given Y is not 0, this will fail.
The above is however still not sufficient, since it will produce a result, but then get stuck in an infinite loop since if it calls (eventually) mult(0, 4, Z) (4 is here just a value), there are two ways to resolve this: with the base case, and with the recursive case.
We thus need a "guard" for the second case, like:
mult(0, _, 0).
mult(X, Y, Z) :-
X > 0,
X1 is X - 1,
mult(X1, Y, Z1),
Z is Y + Z1.
We then obtain for example:
?- mult(14, 25, Z).
Z = 350 ;
false.
One can improve the speed of this mult/3 predicate by implementing a version with an accumulator. I leave this as an exercise.

Swapping binary trees in prolog

This is exercise 3.5 from Learn Prolog Now. They put it before explaining lists so I need a procedure that doesn't involve lists.
The task is to swap the leaves of nested binary trees. If the query is
swap(tree(tree(leaf(1), leaf(2)), leaf(4)), T).
the answer should be
T = (tree(leaf(4), tree(leaf(2), leaf(1))).
With
swap((X, Y), (Y, X)).
swap(tree(X, Y), T) :-
swap((X, Y), (Y, X)),
T = (Y, X).
I get
T = (leaf(4), tree(leaf(1), leaf(2))).
As you see the leaf(1) and leaf(2) didn't get swapped. I want some hints or even your procedure and it should work with any depth of the nodes. Thanks.
You have a base case swap a leaf, and a general case swap a tree!
For a leaf, nothing to do :
swap(leaf(X), leaf(X)).
When you swap a tree, you must swap its leaves too, so
swap(tree(X,Y), tree(Y1,X1)) :-
swap(X,X1),
swap(Y,Y1).

Recursion in Prolog - Finding Path Between Cities

I'm trying to work my way through the exercises at the bottom of this page and I find myself utterly confused on number 3.
We are given the following knowledge base of travel information:
byCar(auckland, hamilton).
byCar(hamilton, raglan).
byCar(valmont, saarbruecken).
byCar(valmont, metz).
byTrain(metz, frankfurt).
byTrain(saarbruecken, frankfurt).
byTrain(metz, paris).
byTrain(saarbruecken, paris).
byPlane(frankfurt, bangkok).
byPlane(frankfurt, singapore).
byPlane(paris, losAngeles).
byPlane(bangkok, auckland).
byPlane(singapore, auckland).
byPlane(losAngeles, auckland).
It's simple to find out if it's possible to travel between two cities. I just did this:
connected(X, Y) :- byCar(X, Y); byTrain(X, Y); byPlane(X, Y).
travel(X, Y) :- connected(X, Y).
travel(X, Z) :- connected(Y, Z), travel(X, Y).
However, when I have to actually unify the path with a variable, I am utterly confused!
I wrote this:
connected(X, Y) :- byCar(X, Y); byTrain(X, Y); byPlane(X, Y).
connected(Y, Z, Out) :- connected(Y, Z).
travel(X, Y, Out) :- connected(X, Y).
travel(A, Z, Out) :- connected(Y, Z),travel(A, Y, connected(Y, Z, Out)).
And called travel(valmont, losAngeles,X).
There is a point during the trace where the correct path shows up, aside from the anonymous variable at the end:
travel(valmont, metz, connected(metz, paris, connected(paris, losAngeles, _17)))
but I don't actually know how to unify this with the variable X!
I can't really wrap my mind around this. Can anyone give me a hint just to push me in the right direction? Is there just a termination condition I'm missing or something?
Edit:
Now I have:
connected(X,Y) :- byCar(X,Y);byTrain(X,Y);byPlane(X,Y).
go(X,Y) :- connected(X,Y).
travel(X,Y,go(X,Y)) :- connected(X,Y).
travel(A,Z,Path) :- travel(Y,Z,Path),go(A,Y,Path).
go(A,Y,Path) :- travel(A,Y,Path).
but it gets stuck like this:
4 4 Exit: byPlane(paris,losAngeles) ?
3 3 Exit: connected(paris,losAngeles) ?
2 2 Exit: travel(paris,losAngeles,go(paris,losAngeles)) ?
5 2 Call: go(metz,paris,go(paris,losAngeles)) ?
6 3 Call: travel(metz,paris,go(paris,losAngeles)) ?
7 4 Call: travel(_217,paris,go(paris,losAngeles)) ?
8 5 Call: travel(_242,paris,go(paris,losAngeles)) ?
9 6 Call: travel(_267,paris,go(paris,losAngeles)) ?
10 7 Call: travel(_292,paris,go(paris,losAngeles)) ?
I've played around with it, but I can't get it to build the whole go(a,b,go(b,c)) etc...
I'll give you the base case of the recursion:
travel(X, Y, go(X, Y)) :- connected(X, Y).
The recursive case looks extremely similar, except that the go/3 term you're building must have locations as its first two arguments, and a path (another go/2 or go/3 term) as its second.
I had this explained to me by aBathologist the following way:
Your objective is to get X = go(valmont,metz,go(metz,paris,go(paris,losAngeles))) in response to the query travel(valmont,losAngeles,X).
To solve this problem, your travel/3 predicate needs to have a From, To, and Path, but it must end with a simple go(From, To) without the Path. The simple go(From, To) is your base condition of travel/3, so:
travel(X, Y, go(X, Y)) :- connected(X, Y).
This is exactly as larsmans states.
Now, you need to create your recursive travel/3 predicate:
travel(X, Y, go(X, Z, Path)) :-
connected(X, Z),
travel(Z, Y, Path).
Your go/2 predicate is redundant and a little confusing given the travel/3 predicate has something that looks like a go predicate. By removing the go/2 predicate, the code is a little easier to read and understand.

power function in prolog

What is wrong with my power function?
pow(_,0,1).
pow(X,Y,Z) :-
pow(X,Y-1,X*Z).
?- pow(2,3,Z).
ERROR: Out of global stack
Your Y does not get decremented, you can not use predicates like functions. You also have to unify Z with the result of the multiplication.
pow(_,0,1).
pow(X,Y,Z) :- Y1 is Y - 1,
pow(X,Y1,Z1), Z is Z1*X.
There is also a builtin power function which will be much faster:
pow2(X,Y,Z) :- Z is X**Y.
Also note that pow is not a last call and can not be optimized to use only one stack frame. You should reformulate it to:
pow3(X,Y,Z) :- powend(X,Y,1,Z),!.
powend(_,0,A,Z) :- Z is A.
powend(X,Y,A,Z) :- Y1 is Y - 1, A1 is A*X, powend(X,Y1,A1,Z).
Predicates
fac(Integer,Integer,Integer).
Clauses
fac(X,N,X):- N=1,!.
fac(X,N,M):- N1=N-1,fac(X,N1,M1), M= X*M1.
Goal
fac(5,3,X).
DOMAINS
num=INTEGER
PREDICATES
nondeterm power(num,num,num)
CLAUSES
power(X,0,1).
power(X,P,F):-X>0,P1=P-1,power(X,P1,F1),F=X*F1.
GOAL
power(2,5,X).

Resources