Prolog successor notation yields incomplete result and infinite loop - prolog

I start to learn Prolog and first learnt about the successor notation.
And this is where I find out about writing Peano axioms in Prolog.
See page 12 of the PDF:
sum(0, M, M).
sum(s(N), M, s(K)) :-
sum(N,M,K).
prod(0,M,0).
prod(s(N), M, P) :-
prod(N,M,K),
sum(K,M,P).
I put the multiplication rules into Prolog. Then I do the query:
?- prod(X,Y,s(s(s(s(s(s(0))))))).
Which means finding the factor of 6 basically.
Here are the results.
X = s(0),
Y = s(s(s(s(s(s(0)))))) ? ;
X = s(s(0)),
Y = s(s(s(0))) ? ;
X = s(s(s(0))),
Y = s(s(0)) ? ;
infinite loop
This result has two problems:
Not all results are shown, note that the result X=6,Y=1 is missing.
It does not stop unless I Ctrl+C then choose abort.
So... my questions are:
WHY is that? I tried switching "prod" and "sum" around. The resulting code gives me all results. And again, WHY is that? It still dead-loops though.
HOW to resolve that?
I read the other answer on infinite loop. But I'd appreciate someone answer basing on this scenario. It greatly helps me.

If you want to study termination properties in depth, programs using successor-arithmetics are an ideal study object: You know a priori what they should describe, so you can concentrate on the more technical details. You will need to understand several notions.
Universal termination
The easiest way to explain it, is to consider Goal, false. This terminates iff Goal terminates universally. That is: Looking at tracers is the most ineffective way - they will show you only a single execution path. But you need to understand all of them at once! Also never look at answers when you want universal termination, they will only distract you. You have seen it above: You got three neat and correct answers, only then your program loops. So better "turn off" answers with false. This removes all distraction.
Failure slice
The next notion you need is that of a failure slice. Take a pure monotonic logic program and throw in some goals false. If the resulting failure slice does not terminate (universally), also the original program won't. In your exemple, consider:
prod(0,M,0) :- false.
prod(s(N), M, P) :-
prod(N,M,K), false,
sum(K,M,P).
These false goals help to remove irrelevant adornments in your program: The remaining part shows you clearly, why prod(X,Y,s(s(s(s(s(s(0))))))). does not terminate. It does not terminate, because that fragment does not care about P at all! You are hoping that the third argument will help to make prod/3 terminate, but the fragment shows you it is all in vain, since P does not occur in any goal. No need for chatty tracers.
Often it is not so easy to find minimal failure slices. But once you found one, it is next to trivial to determine its termination or rather non-termination properties. After some time you can use your intuition to imagine a slice, and then you can use your reason to check if that slice is of relevance or not.
What is so remarkable about the notion of a failure slice is this: If you want to improve the program, you have to modify your program in the part visible in above fragment! As long as you do not change it, the problem will persist. A failure slice is thus a very relevant part of your program.
Termination inference
That is the final thing you need: A termination inferencer (or analyzer) like cTI will help you to identify the termination condition rapidly. Look at the inferred termination conditions of prod/3 and the improved prod2/3 here!
Edit: And since this was a homework question I have not posted the final solution. But to make it clear, here are the termination conditions obtained so far:
prod(A,B,C)terminates_if b(A),b(B).
prod2(A,B,C)terminates_if b(A),b(B);b(A),b(C).
So the new prod2/3 is strictly better than the original program!
Now, it is up to you to find the final program. Its termination condition is:
prod3(A,B,C)terminates_if b(A),b(B);b(C).
To start with, try to find the failure slice for prod2(A,B,s(s(s(s(s(s(0)))))))! We expect it to terminate, but it still does not. So take the program and add manuallyfalse goals! The remaining part will show you the key!
As a final hint: You need to add one extra goal and one fact.
Edit: Upon request, here is the failure slice for prod2(A,B,s(s(s(s(s(s(0))))))):
prod2(0,_,0) :- false.
prod2(s(N), M, P) :-
sum(M, K, P),
prod2(N,M,K), false.
sum(0, M, M).
sum(s(N), M, s(K)) :- false,
sum(N,M,K).
Please note the significantly simplified definition of sum/3. It only says: 0 plus anything is anything. No more. As a consequence even the more specialized prod2(A,0,s(s(s(s(s(s(0))))))) will loop whileprod2(0,X,Y) elegantly terminates ...

The first question (WHY) is fairly easy to spot, specially if know about left recursion. sum(A,B,C) binds A and B when C is bound, but the original program prod(A,B,C) doesn't use that bindings, and instead recurse with still A,B unbound.
If we swap sum,prod we get 2 useful bindings from sum for the recursive call:
sum(M, K, P)
Now M is bound, and will be used to terminate the left-recursion. We can swap N and M, because we know that product is commutative.
sum(0, M, M).
sum(s(N), M, s(K)) :-
sum(N, M, K).
prod3(0, _, 0).
prod3(s(N), M, P) :-
sum(M, K, P),
prod3(M, N, K).
Note that if we swap M,K (i.e. sum(K,M,P)), when prod3 is called with P unknown we again have a non terminating loop, but in sum.
?- prod3(X,Y,s(s(s(s(s(s(0))))))).
X = s(s(s(s(s(s(0)))))),
Y = s(0) ;
X = s(s(s(0))),
Y = s(s(0)) ;
X = s(s(0)),
Y = s(s(s(0))) ;
X = s(0),
Y = s(s(s(s(s(s(0)))))) ;
false.
OT I'm perplexed by cTI report: prod3(A,B,C)terminates_if b(A),b(B);b(A),b(C).

Related

Prolog factorial predicate

I have a factorial predicatefact(N,F), where either N or F or both are bounded to a number.
For example I can have fact(3,F) or fact(N,6).
Here is my predicate, which works but I don't really understand how. I used trace but still have problems understanding it.
fact(0,1).
fact(N,F) :-
fact(N1,F1),
N is N1 + 1,
F is N * F1.
You can try to go through your program step-by-step to understand what's going on. This will be very slow indeed, and quite unreliable. Or, you can instead let Prolog do (part of) the work. So the idea is to modify the program a bit and then look what Prolog thinks about it.
This is what I see when I look at your program - this is called a failure-slice
fact(0,1) :- false.
fact(N,F) :-
fact(N1,F1), false,
N is N1 + 1,
F is N * F1.
When will this fragment terminate? Look at the remaining visible part! The N only occurs once in the head: nobody is interested in the first argument! Same for the F. Therefore: No matter what arguments you have, this program will not terminate. And thus the same holds for your original program!
In the original version this wasn't that clear. Watch out:
?- fact(29,F).
F = 8841761993739701954543616000000
At first this looks nice, but if you ask for the next answer (with SPACE or ;), you end up looping, i.e. waiting for an answer that never comes. Worse, false queries loop right away:
?- fact(29,1).
loops.
So how can you find these problems, without understanding precisely what is going on? This is what false is for. A goal that is never true. If you add it like fact(29,F), false. you will never be distracted by beautiful answers.
Why have you put all your arithmetics at the end? I suspect because you got some errors before. There is an easy way out to avoid all such errors:
:- use_module(library(clpfd)).
You can now write #= instead of is, and you need some restriction like N #>= 1. Can I leave you there?

Prolog and limitations of backtracking

This is probably the most trivial implementation of a function that returns the length of a list in Prolog
count([], 0).
count([_|B], T) :- count(B, U), T is U + 1.
one thing about Prolog that I still cannot wrap my head around is the flexibility of using variables as parameters.
So for example I can run count([a, b, c], 3). and get true. I can also run count([a, b], X). and get an answer X = 2.. Oddly (at least for me) is that I can also run count(X, 3). and get at least one result, which looks something like X = [_G4337877, _G4337880, _G4337883] ; before the interpreter disappears into an infinite loop. I can even run something truly "flexible" like count(X, A). and get X = [], A = 0 ; X = [_G4369400], A = 1., which is obviously incomplete but somehow really nice.
Therefore my multifaceted question. Can I somehow explain to Prolog not to look beyond first result when executing count(X, 3).? Can I somehow make Prolog generate any number of solutions for count(X, A).? Is there a limitation of what kind of solutions I can generate? What is it about this specific predicate, that prevents me from generating all solutions for all possible kinds of queries?
This is probably the most trivial implementation
Depends from viewpoint: consider
count(L,C) :- length(L,C).
Shorter and functional. And this one also works for your use case.
edit
library CLP(FD) allows for
:- use_module(library(clpfd)).
count([], 0).
count([_|B], T) :- U #>= 0, T #= U + 1, count(B, U).
?- count(X,3).
X = [_G2327, _G2498, _G2669] ;
false.
(further) answering to comments
It was clearly sarcasm
No, sorry for giving this impression. It was an attempt to give you a synthetic answer to your question. Every details of the implementation of length/2 - indeed much longer than your code - have been carefully weighted to give us a general and efficient building block.
There must be some general concept
I would call (full) Prolog such general concept. From the very start, Prolog requires us to solve computational tasks describing relations among predicate arguments. Once we have described our relations, we can query our 'knowledge database', and Prolog attempts to enumerate all answers, in a specific order.
High level concepts like unification and depth first search (backtracking) are keys in this model.
Now, I think you're looking for second order constructs like var/1, that allow us to reason about our predicates. Such constructs cannot be written in (pure) Prolog, and a growing school of thinking requires to avoid them, because are rather difficult to use. So I posted an alternative using CLP(FD), that effectively shields us in some situation. In this question specific context, it actually give us a simple and elegant solution.
I am not trying to re-implement length
Well, I'm aware of this, but since count/2 aliases length/2, why not study the reference model ? ( see source on SWI-Prolog site )
The answer you get for the query count(X,3) is actually not odd at all. You are asking which lists have a length of 3. And you get a list with 3 elements. The infinite loop appears because the variables B and U in the first goal of your recursive rule are unbound. You don't have anything before that goal that could fail. So it is always possible to follow the recursion. In the version of CapelliC you have 2 goals in the second rule before the recursion that fail if the second argument is smaller than 1. Maybe it becomes clearer if you consider this slightly altered version:
:- use_module(library(clpfd)).
count([], 0).
count([_|B], T) :-
T #> 0,
U #= T - 1,
count(B, U).
Your query
?- count(X,3).
will not match the first rule but the second one and continue recursively until the second argument is 0. At that point the first rule will match and yield the result:
X = [_A,_B,_C] ?
The head of the second rule will also match but its first goal will fail because T=0:
X = [_A,_B,_C] ? ;
no
In your above version however Prolog will try the recursive goal of the second rule because of the unbound variables B and U and hence loop infinitely.

Prolog: redundant program points in failure-slice?

We are implementing diagnostic tools for explaining unexpected universal non-termination in pure, monotonic Prolog programs—based on the concept of the failure-slice.
As introduced in
the paper "Localizing and explaining reasons for nonterminating logic programs with failure slices", goals false/0 are added at a number of program points in an effort to reduce the program fragment sizes of explanation candidates (while still preserving non-termination).
So far, so good... So here comes my question1:
Why are there N+1 program points in a clause having N goals?
Or, more precisely:
How come that N points do not suffice? Do we ever need the (N+1)-th program point?
Couldn't we move that false to each use of the predicate of concern instead?
Also, we know that the program fragment is only used for queries like ?- G, false.
Footnote 1: We assume each fact foo(bar,baz). is regarded as a rule foo(bar,baz) :- true..
Why are there N+1 program points in a clause having N goals? How come that N points do not suffice?
In many examples, not all points are actually useful. The point after the head in a predicate with a single clause is such an example. But the program points are here to be used in any program.
Let's try out some examples.
N = 0
A fact is a clause with zero goals. Now even a fact may or may not contribute to non-termination. As in:
?- p.
p :-
q(1).
p.
q(1).
q(2).
We do need a program point for each fact of q/1, even if it has no goal at all, since the minimal failure slice is:
?- p, false.
p :-
q(1),
p, false.
q(1).
q(2) :- false.
N = 1
p :-
q,
p.
p :-
p.
q :-
s.
s.
s :-
s.
So here the question is: Do we need two program points in q/0? Well yes, there are different independent failure slices. Sometimes with false in the beginning, and sometimes at the end.
What is a bit confusing is that the first program point (that is the one in the query) is always true, and the last is always false. So one could remove them, but I think it is clearer to leave them, as a false at the end is what you have to enter into Prolog anyway. See the example in the Appendix. There, P0 = 1, P8 = 0 is hard coded.

Prolog: Check if X is in range of 0 to K - 1

I'm new to prolog and every single bit of code I write turns into an infinite loop.
I'm specifically trying to see if X is in the range from 0 to K - 1.
range(X,X).
range(X,K) :- K0 is K - 1, range(X,K0).
My idea behind the code is that I decrement K until K0 equals X, then the base case will kick in. I'm getting an infinite loop though, so something with the way I'm thinking is wrong.
Welcome to the wondrous world of Prolog! It seems you tried to leapfrog several steps when learning Prolog, and (not very surprisingly) failed.
Ideally, you take a book like Art of Prolog and start with family relations. Then extend towards natural numbers using successor-arithmetics, and only then go to (is)/2. Today, (that is, since about 1996) there is even a better way than using (is)/2 which is library(clpfd) as found in SICStus or SWI.
So let's see how your program would have been, using successor-arithmetics. Maybe less_than_or_equal/2 would be a better name:
less_than_or_equal(N,N).
less_than_or_equal(N,s(M)) :-
less_than_or_equal(N,M).
?- less_than_or_equal(N,s(s(0))).
N = s(s(0))
; N = s(0)
; N = 0.
It works right out of the box! No looping whatsoever. So what went wrong?
Successor arithmetics relies on the natural numbers. But you used integers which contain also these negative numbers. With negative numbers, numbers are no longer well ordered (well founded, or Noetherian), and you directly experienced that consequence. So stick with the natural numbers! They are all natural, and do not contain any artificial negative ingredients. Whoever said "God made the integers, all else is the work of man." must have been wrong.
But now back to your program. Why does it not terminate? After all, you found an answer, so it is not completely wrong. Is it not? You tried to reapply the notions of control flow you learned in command oriented languages to Prolog. Well, Prolog has two relatively independent control flows, and many more surprising things like real variables (TM) that appear at runtime that have no direct counterpart in Java or C#. So this mapping did not work. I got a little bit suspicious when you called the fact a "base case". You probably meant that it is a "termination condition". But it is not.
So how can we easily understand termination in Prolog? The best is to use a failure-slice. The idea is that we will try to make your program as small as possible by inserting false goals into your program. At any place. Certain of the resulting programs will still not terminate. And those are most interesting, since they are a reason for non-termination of the original program! They are immediately, causally connected to your problem. And they are much better for they are shorter. Which means less time to read. Here are some attempts, I will strike through the parts that are no longer relevant.
range(X,X).
range(X,K) :-
K0 is K - 1, false,
range(X,K0).
Nah, above doesn't loop, so it cannot tell us anything. Let's try again:
range(X,X) :- false.
range(X,K) :-
K0 is K - 1,
range(X,K0), false.
This one loops for range(X,1) already. In fact, it is the minimal failure slice. With a bit of experience you will learn to see those with no effort.
We have to change something in the visible part to make this terminate. For example, you might add K > 0 or do what #Shevliaskovic suggested.
I believe the simplest way to do this is:
range(X,X).
range(X,K) :- X>0, X<K-1.
and here are my results:
6 ?- range(4,4).
true .
7 ?- range(5,8).
true.
8 ?- range(5,4).
false.
The simple way, as has been pointed out, if you just want to validate that X lies within a specified domain would be to just check the condition:
range(X,K) :- X >= 0 , X < K .
Otherwise, if you want your range/2 to be generative, would be to use the built-in between/3:
range(X,K) :- integer(K) , K1 is K-1 , between(0,K1,X).
If your prolog doesn't have a between/3, it's a pretty simple implementation:
%
% the classic `between/3` wants the inclusive lower and upper bounds
% to be bound. So we'll make the test once and use a helper predicate.
%
between(Lo,Hi,N) :-
integer(Lo),
integer(Hi),
_between(Lo,Hi,N)
.
_between(Lo,Hi,Lo) :- % unify the lower bound with the result
Lo =< Hi % - if we haven't yet exceeded the inclusive upper bound.
. %
_between(Lo,Hi,N) :- % otherwise...
Lo < Hi , % - if the lower bound is less than the inclusive upper bound
L1 is Lo+1 , % - increment the lower bound
_between(L1,Hi,N) % - and recurse down.
. %

To count down from a number using Prolog

Trivial question but I want the program to return a list of the numbers less than or equal to a given number. For example, CountD(4,L). should give [4,3,2,1]. This is what I have so far:
CountD(1,[]).
CountD(Num,List):- [Num|List], CountD(M,List), Num is M+1.
This is also a solution:
countd(N, [N|List0]) :-
succ(N0, N),
countd(N0, List0).
countd(0, []).
Here, the important line is succ(N0, N). This will only succeed for N > 0, and will fail when the second argument is 0 (it will also raise an error if N is not a non-negative integer).
When succ(N0, N) fails, the second clause will be evaluated. It will only succeed when its arguments are 0 and the empty list.
#Boris' answer is perfect. I would just like to explain, why your original program, and that of another answer does not terminate.
At first sight, it seems to work, after all we get an answer:
?- countD(4,L).
L = [4, 3, 2, 1]
But note that Prolog showed us only the first answer, there are more, waiting ...
?- countD(4,L).
L = [4, 3, 2, 1]
; loops.
The best way to understand termination in Prolog is to concentrate on the relevant parts of your program. And the nice thing in Prolog is that while its actual control flow is quite complex, it often suffices to look at a very tiny part of your program to determine certain properties. So there is no need to "understand everything at once".
Instead of looking at everything, I will take the following failure-slice:
countD(0,[]) :- false.
countD(Num,List) :-
List = [Num|L],
countD(M,L), false,
Num is M+1.
In the first line, by inserting false at the fact, the fact is effectively removed. So no matter what it describes exactly, the 0 or 1 does not have any influence on the termination of this predicate. In general, non-termination of any failure-slice implies non-termination of the original program.
Of course we have to find the right slice. That's a bit of experience. But once it is found, its termination properties are easy to check.
To fix this problem, we can say immediately that adding a further clause will not improve termination (provided the program is a pure, monotonic one). We need to change something in the visible part. Boris did this by adding some check for Num before the actual recursion.
countD(0,[]).
countD(Num,List) :- List = [Num|L], countD(M,L), Num is M+1.
UPDATED:
Fixes problems found by #false and #boris in comments:
countD(Num,[Num|L]) :- Num > 0, M is Num-1, countD(M,L).
countD(0,[]).

Resources