Why do I get a Infinite Loop (Prolog) - prolog

I am just starting to learn Prolog and I played around with it. Now I got to a point where I´m stuck. The program i wrote gets into an infinite loop when I ask for
?- q(b).
and I don´t understand why it does that. It would be nice if someone could explain it to me.
p(a).
p(b).
q(Y) :- r(X), r(Y).
r(X) :- r(f(X)).
r(a) :- p(c).
r(a) :- p(a).
r(b) :- p(b).

As said in the comment, the loop is caused by r/1. To show why, yust type ?- trace, q(b). Look at the trace (ignore by now the singleton warning):
Call:q(b)
Call:r(_4244)
Call:r(f(_4162))
Call:r(f(f(_4162)))
Call:r(f(f(f(_4162))))
Call:r(f(f(f(f(_4162)))))
Call:r(f(f(f(f(f(_4162))))))
Call:r(f(f(f(f(f(f(_4162)))))))
Call:r(f(f(f(f(f(f(f(_4162))))))))
Now you can see that it try to derives r/1 entering a loop. You can see also this question to have a more in depth explaination.
Notice that in prolog, the order of the clauses matters. Just try to put the line r(X) :- r(f(X)). to the bottom of your program. Now try ?- q(b). On the first answer you get true because prolog unifies X with a and Y with b before entering in a loop.

Another way to identify reasons for non-termination is to reduce the number of inferences your program will execute by adding goals false into your program:
q(Y) :- r(X), false, r(Y).
r(X) :- r(f(X)), false.
r(a) :- false, p(c).
r(a) :- false, p(a).
r(b) :- false, p(b).
?- q(Y).
loops.
Since this program is still looping, you will need to modify something in the visible part. Note how many things have been removed entirely! No matter how p/1 is defined, this problem will persist.
If you look at q/1 closely, you see one of the problems:
q(Y) :- r(X), false, r(Y).
The variable Y is not used in the visible part at all. The X appears just once. Thus, r(X) will be the most general query possible and thus it will have the worst termination property possible (that depends on the definition of r/1, indeed). In any case, the argument of q/1 has no influence on termination!
There is another property to conclude: The order of clauses does not have any influence on the termination property! You can see this easily: No matter where the clauses that have been removed entirely with false appear, they can be removed.
For more, see failure-slice.

Related

Why does my list reversal only work correctly in one direction?

I have produced the following code.
list_reverse([],[]).
list_reverse([X],[X]).
list_reverse(Ls,[R|Rs]) :-
last_elem(Ls,R),
without_last_elem(Ls,Next),
list_reverse(Next,Rs).
last_elem([E],E).
last_elem([_|Xs],E) :-
last_elem(Xs,E).
without_last_elem([X,_|[]],[X|[]]).
without_last_elem([X|T0],[X|T1]) :-
without_last_elem(T0,T1).
Swipl:
?- list_reverse([1,2,3],X).
X = [3, 2, 1] ;
false.
This is exactly what I want.
However if I go in the opposite direction I get success, followed by non-termination.
?- list_reverse(X,[1,2,3]).
X = [3, 2, 1] ;
C-c C-cAction (h for help) ? a
abort
% Execution Aborted
What I am struggling to understand is why I first get a correct solution for X. Is my program correct or not?
I am not worried about reversing a list as much as I am about this pattern of getting a correct solution followed by non-termination. It is a pattern I have already come across a few times.
I am [worried] about this pattern of getting a correct solution followed by non-termination.
This is due to the very specific notion of (universal) termination in Prolog. In other programming languages termination is a much simpler beast (still an undecidable beast nevertheless). If, say, a function returns then it terminates (for that case). But in Prolog, producing an answer is not the end as there might be further solutions or just an unproductive loop. In fact, it's best not to consider your query ?- list_reverse(X,[1,2,3]). but rather the following instead.
?- list_reverse(X,[1,2,3]), false.
In this manner all distracting answers are turned off. The only purpose of this query is now either to show termination or non-termination.
After that,
you can either try to follow Prolog's precise execution path but that is as insightful as staring into a car's gearbox when you are lost (the gears caused you to move into the place where you are lost thus they are somehow the cause...). Or, you take a step back, and consider related program fragments (called slices) that share certain properties with your original program. For termination, a failure-slice helps you to better understand what is at stake. In your case consider:
list_reverse([],[]) :- false.
list_reverse([X],[X]) :- false.
list_reverse(Ls,[R|Rs]) :-
last_elem(Ls,R), false,
without_last_elem(Ls,Next),
list_reverse(Next,Rs).
last_elem([E],E) :- false.
last_elem([_|Xs],E) :-
last_elem(Xs,E), false.
?- list_reverse(X,[1,2,3]), false.
Since this failure slice does not terminate, also your original program doesn't terminate! And, it is much easier to reason here in this smaller fragment. If you want to fix the problem, you need to modify something in the visible part. Otherwise you will keep being stuck in a loop.
Note that none of the facts is part of the loop. Thus they are irrelevant for non-termination.
Also note that in list_reverse/2 the variable Rs is never used in the visible part. Thus Rs has no influence on termination! Please note that this is a proof of that property already. Does this mean that the second argument of list_reverse/2 has no influence on termination? What do you think?
The last_elem/2 can keep constructing larger lists, that all should be rejected. But you thus get stuck in an infinite loop.
We can make a function that works with accumulator, and iterates over both the two lists concurrently. That means that once the left or right list is exhausted, no more recursive calls will be made:
reverse(L1, L2) :-
reverse(L1, [], L2, L2).
reverse([], L, L, []).
reverse([H|T], L1, R, [_|T2]) :-
reverse(T, [H|L1], R, T2).
Here the [H|T] and [_|T2] pattern thus both pop the first item of the list, and we only match if both lists are exhausted.

Termination condition prolog

My teacher provided us with some slides regarding Prolog and I found something a bit weird.
reverse([],[]).
reverse([X|Xs],Zs) :- reverse(Xs,Ys), append(Ys, [X], Zs).
According to him, the program terminates when the 1st argument reverse([],..) is a complete list.
Furthermore, if you switch the objectives within the predicate to reverse([X|Xs],Zs) :- append(Ys, [X], Zs), reverse(Xs,Ys). the program should terminate when the 2nd argument is a complete list reverse(..,[]).
This goes a bit against what i've learnt so far. I thought that both of the arguments influenced the program's termination condition and apparently they do not according to my teacher's example.
Can anyone give me some input on this?
The termination property of Prolog programs is a bit difficult to grasp due to Prolog's relatively complex control flow. There are ways to cut down the complexity. One, is to consider only parts of your program by inserting additional goals false into your program. You can place them in any place. And no matter where you place them the following holds: If this new program, called a failure-slice does not terminate, then also your original program does not terminate. Note the "if". Here is your program:
reverse([],[]) :- false.
reverse([X|Xs],Zs) :-
reverse(Xs,Ys), false,
append(Ys, [X], Zs).
This failure-slice is completely useless for understanding what your relation describes. It will never succeed. However, it helps us to better understand how termination will do.
Note that the fact is completely eliminated. In fact, no matter what the fact looks like it cannot improve termination of this failure-slice of reverse/2. (It could deteriorate termination, though).
Also note the second argument, the Zs: there is no further mentioning of this Zs. Thus: the second argument of reverse/2 can be whatever it wants to be. It will again not improve termination.
To make reverse/2 terminate, the first argument has to be instantiated such that this fragment will terminate. Thus [], [X|[]] will terminate, even [X|nonlist] will terminate. But partial lists such as Xs, [a|Xs] etc will not terminate.
If you want improve termination for, say, reverse(Xs,[]) you need to change something in the visible, remaining part. One way is to exchange the goals. Now, Zs may contribute to the termination. But -alas- the first argument has now no longer the influence it used to have. Consider reverse([a], Zs) and:
reverse([],[]) :- false.
reverse([X|Xs],Zs) :-
append(Ys, [X], Zs), false,
reverse(Xs,Ys).
append([], Zs, Zs) :- false.
append([X|Xs], Ys, [X|Zs]) :-
append(Xs, Ys, Zs), false.
So while this fragment still insists that the first argument is [_|_] it does not take the remaining term into account. And thus, the goal will not terminate.
If you want to learn more, look at failure-slice. Equally, consider using cTI. To develop a good intuition, you will need to try it out yourself.

Prolog: for what terms t does the evaluation of the goal p(t) terminate and which does it not?

Consider the following logic program:
p(b) :- p(b).
p(X) :- r(b).
p(a) :- p(a).
r(Y).
For what terms t does the evaluation of the goal p(t) terminate and which does it not?
What you want is to determine all the queries that do not terminate. And I assume you mean terminate universally. That is, we are not only looking at the first answer, but we look at all of them.
There is a very quick answer to this, provided your program is pure, monotonic: Simply take the most general query. That is, if there is any possibility for any term T to make p(T) not terminating, then p(X) will be non-terminating too.
If we want to known more, we have to look closer. With a failure-slice we may narrow down actual reasons for non-termination. By inserting goals false into your program, we are reducing the number of inferences that are possible for a query. But if the remaining program still permits infinitely many inferences, we have found a loop. In this case:
p(b) :- false, p(b).
p(X) :- false, r(b).
p(a) :- p(a), false.
r(Y) :- false.
Is one minimal failure-slice. That is, p(a) will not terminate. Beware, however, that
simply querying p(a) (in your original program) will succeed, you need to insist to look at further answers:
?- p(a).
true % there is one answer
; loops. % loops "on backtracking"
To save you the labor of asking for further answers, simply use p(a), false instead.
There is another minimal failure-slice:
p(b) :- p(b), false.
p(X) :- false, r(b).
p(a) :- false, p(a).
r(Y) :- false.
See failure-slice for more examples.

How does negation-as-failure works in Prolog?

I want to know how Prolog solves this program:
test(X, Y).
test(X, X):-!, fail.
I googled "negation as failure" but I am confused!
Consider the following example:
father(nick, john).
We use the predicate father(X,Y) to denote that the father of X is Y.
Let's query the database:
?- father(nick,X).
X = john.
?- father(john,Y).
false.
In both cases we asked who is the father of someone (nick, john respectively). In the first case, prolog knew the answer (john) however in the second it didn't and so the answer was false, meaning that john does not have any father. We might expect that, as we gave prolog no information about john's father, it would respond with unknown. That would be an open-world where if something is not known we don't assume that it's false. On the contrary, in the closed world of prolog, if we don't know something, we assume that it's false.
Note that a world where we say that we don't know who the father of john is, based on knowing that anyone must have a father is not an open world; it can be easily modelled in prolog:
data_father(nick, john).
father(X,Y):-
data_father(X,Y) -> true ; true.
On the other hand, in an open world prolog you would write facts and counter facts:
father(nick, john).
not father(adam, X).
And this is negation as failure. However, this is not what happens in your program:
test(X, Y).
test(X, X):-!, fail.
The first clause will always succeed, regardless of the value of the arguments. In fact, exactly because of that, there is no point in naming the arguments and prolog will give you a singleton warning; you can write the clause as test(_, _).
On the other hand, the second clause will always fail. It can fail in two ways: (1) the arguments may be different (2) the arguments are unifiable so prolog moves to the body and then fails.
Precisely because prolog is using a closed world model there is no point of having clauses (without side-effects (but that's considered bad practise anyway)) that always fail. On the contrary, these extra calls cause your program to run slower and use more memory.
It is also worth noting that the cut (!/0) does nothing here since when you reach it there are no more choice points. Consider however this example:
test(X, Y).
test(X, X):-!, fail.
test(X, 42).
?- test(1,42).
true ;
true.
?- test(42,42).
true ;
false.
In both cases prolog will create 3 choice points, one for each clause.
In the first case, Prolog will successfully match the head of the first clause and succeed since there is no body.
Then, it will fail matching the head of the second clause and the body will not be "executed".
Finally, it will match the head of the third clause and succeed since there is no body.
However, on the second case:
Prolog will succeed in matching the head of the first clause and succeed since there is no body.
Then, it will succeed in matching the head of the second clause; the cut will remove all other choice points and then it will fail due to fail.
Therefore, prolog will not try the third clause.
A few words about negation as failure since you mentioned it. Negation as failure is based on the closed world assumption; since we assume that anything that cannot be deduced from the facts we already have is wrong, if we fail to prove something it means that the opposite of it is considered true. For example, consider this:
father(nick, john).
fatherless(X) :- \+ father(X, _).
And
?- fatherless(nick).
false.
?- fatherless(john).
true.
On the contrary, in an open world prolog with the following code:
father(nick, john).
not father(adam, X).
fatherless(X) :- \+ father(X, _).
fatherless/1 would succeed only for adam, fail for nick and return unknown for anything else
the first clause test(X, Y). says that test/2 is unconditionally true, for whatsoever argument pattern.
the second clause test(X, X):-!, fail. says that, when test/2 is called with unifiable first and second argument, there are not more alternative, then fail (note that will fail always, because argument schema is ruling out the instantiation pattern where first argument \= second implicitly).
The operational effect if the same as a logical negation, under 'Closed World Assumption'.

Prolog: Error out of global stack with what looks like ONE level of recursion to me

I am quite rusty in prolog, but I am not sure why things like this fail:
frack(3).
frack(X) :- frack(X-1).
So, if I evaluate frack(4). from the interactive prompt with the above facts defined, I expect that it should not have to endlessly recurse, since 4-1 = 3. But I get this error in SWI-Prolog:
ERROR: Out of global stack
Try it:
?- 4-1 = 3.
false.
Why? Because 4-1 = -(4, 1), which clearly is not a number but a compound term.
To reason about integers in Prolog, use clpfd constraints, for example (using GNU Prolog or B-Prolog):
| ?- 4-1 #= X.
X = 3
In SWI-Prolog, the graphical tracer may be useful for you to see what happens:
?- gtrace, frack(4).
For more complex debugging, I recommend failure-slice as shown in false's answer.
Here is the reason for this non-termination. Your query does not terminate, because there is a failure-slice of your program that does not terminate:
?- frack(4).
frack(3) :- false.
frack(X) :-
frack(X-1), false.
You can fix this only by modifying something in the visible part. Three SO-answers suggest to use (is)/2. But this will not remove non-termination! In fact, using (is)/2 leads to essentially the same fragment:
?- frack(4).
frack(3) :- false.
frack(X) :-
Y is X - 1,
frack(Y), false.
At least, frack(4) now succeeds, but it will loop on backtracking. You have to change something in the visible part, like some test for X, in order to avoid non-termination. See failure-slice for more.
frack(X) :- frack(X-1).
should be
frack(X) :- Y is X - 1, frack(Y).
The way you wrote it, X-1 expression of the first level unifies with X variable at the next level, never going for the frack(3) fact.
Prolog doesn't do arithmetic unless you use the is operator:
frack(X) :- X1 is X-1, frack(X1).

Resources