Understanding recursivity in Prolog - prolog

I have this example:
descend(X,Y) :- child(X,Y).
descend(X,Y) :- child(X,Z), descend(Z,Y).
child(anne,bridget).
child(bridget,caroline).
child(caroline,donna).
It works great and I understand it. This is a solution of a little exercise. My solution was the same but changing:
descend(X,Y) :- descend(X,Z), descend(Z,Y).
That is, changing child for descend in the second descend rule.
If I query descend(X, Y). in the first solution, I obtain:
?- descend(X, Y).
X = anne,
Y = bridget ;
X = bridget,
Y = caroline ;
X = caroline,
Y = donna ;
X = anne,
Y = caroline ;
X = anne,
Y = donna ;
X = bridget,
Y = donna ;
false.
Which is correct. But if I query with my solution the same, I get:
?- descend(X, Y).
X = anne,
Y = bridget ;
X = bridget,
Y = caroline ;
X = caroline,
Y = donna ;
X = anne,
Y = caroline ;
X = anne,
Y = donna ;
ERROR: Out of local stack
It doesn't say X = bridget,Y = donna ; and it also overflows. I understand why it overflows. What I don't understand is why it doesn't find this last relationship. Is it because of the overflow? If so, why? (Why is the stack so big with such small knowledge base?).
If I query descend(bridget, donna) it answers yes.
I'm having problems imagining the exploration tree...
Apart from that question, I guess that the original solution is more efficient (ignoring the fact that mine enters in a infinite loop at the end), isn't it?
Thanks!

I'm having problems imagining the exploration tree...
Yes, that's quite difficult in Prolog. And it would be worse if you had a bigger database! But most of the time it is not necessary to envision the very precise search tree. Instead, you can use several quite robust notions.
Remember how you formulated your query. You looked at one solution after the other. But what you really were interested in was the question whether or not the query terminates. You can go for it without looking at the solutions by adding false.
?- descend(X, Y), false.
ERROR: Out of local stack
This query can never be true. It can either fail, overflow, or loop, or produce another error. What remains is a very useful notion: Universal termination or as in this case non-termination.
This can be extended to your actual program:
descend(X,Y) :- false, child(X,Y).
descend(X,Y) :- descend(X,Z), false, descend(Z,Y).
If this fragment called a failure-slice does not terminate, then also your original program does not terminate. Look at this miserable remainder of your program! Not even child/2 is present any longer. And thus we can conclude that child/2 does not influence non-termination! The Y occurs only once. And X will never cause a failure. Thus descend/2 terminates never!
So this conclusion is much more general than just a statement about a specific search tree. It's a statement about all of them.
If you still want to reason about the very precise order of solutions, you will have to go into the very gore of actual execution. But why bother? It's extremely complex, in particular if your child/2 relation contains cycles. Chances are that you will confuse things and build inaccurate theories (at least I did). No need for another cargo cult. I, for one, have given up to "step through" such myriads of detail. And I do not miss it.

Related

How can Prolog derive nonsense results such as 3 < 2?

A paper I'm reading says the following:
Plaisted [3] showed that it is possible to write formally correct
PROLOG programs using first-order predicate-calculus semantics and yet
derive nonsense results such as 3 < 2.
It is referring to the fact that Prologs didn't use the occurs check back then (the 1980s).
Unfortunately, the paper it cites is behind a paywall. I'd still like to see an example such as this. Intuitively, it feels like the omission of the occurs check just expands the universe of structures to include circular ones (but this intuition must be wrong, according to the author).
I hope this example isn't
smaller(3, 2) :- X = f(X).
That would be disappointing.
Here is the example from the paper in modern syntax:
three_less_than_two :-
less_than(s(X), X).
less_than(X, s(X)).
Indeed we get:
?- three_less_than_two.
true.
Because:
?- less_than(s(X), X).
X = s(s(X)).
Specifically, this explains the choice of 3 and 2 in the query: Given X = s(s(X)) the value of s(X) is "three-ish" (it contains three occurrences of s if you don't unfold the inner X), while X itself is "two-ish".
Enabling the occurs check gets us back to logical behavior:
?- set_prolog_flag(occurs_check, true).
true.
?- three_less_than_two.
false.
?- less_than(s(X), X).
false.
So this is indeed along the lines of
arbitrary_statement :-
arbitrary_unification_without_occurs_check.
I believe this is the relevant part of the paper you can't see for yourself (no paywall restricted me from viewing it when using Google Scholar, you should try accessing this that way):
Ok, how does the given example work?
If I write it down:
sm(s(s(s(z))),s(s(z))) :- sm(s(X),X). % 3 < 2 :- s(X) < X
sm(X,s(X)). % forall X: X < s(X)
Query:
?- sm(s(s(s(z))),s(s(z)))
That's an infinite loop!
Turn it around
sm(X,s(X)). % forall X: X < s(X)
sm(s(s(s(z))),s(s(z))) :- sm(s(X),X). % 3 < 2 :- s(X) < X
?- sm(s(s(s(z))),s(s(z))).
true ;
true ;
true ;
true ;
true ;
true
The deep problem is that X should be Peano number. Once it's cyclic, one is no longer in Peano arithmetic. One has to add some \+cyclic_term(X) term in there. (maybe later, my mind is full now)

Does it matter what is unification terms sequence?

Lets consider I have two terms T1 and T2. I unified two terms and got result.
My question is: If I change places of terms and unify terms T2 and T1 - whether result will be the same or different?
I tried to change terms and got the same result. But in theory I can read: in Prolog sequence is important.
So how do you think - is the result the same or different and why?
A difference that shows simply by replacing X = Y by Y = X is highly unlikely.
As long as you consider syntactic unification (using the occurs-check) or rational tree unification, the only differences might be some minimal performance differences.
More visible differences are surfacing when going beyond these well defined relations:
when mixing both, unification may not terminate. I can only give you somewhat related examples in SWI and Scryer:
?- X = s(X), unify_with_occurs_check(X, s(X)).
X = s(X).
?- unify_with_occurs_check(X, s(X)), X = s(X).
false.
Above, commutativity of goals is broken. But then, we are mixing two incompatible theories with one another. So, we can't really complain.
?- Y = s(Y), unify_with_occurs_check(X-X,s(X)-Y).
false.
?- Y = s(Y), unify_with_occurs_check(X-X,Y-s(X)).
Y = s(Y), X = s(Y) % Scryer
| Y = X, X = s(X). % SWI
And here we just exchange the order of arguments. It is general intuition that exchanging (consistently) arguments of a functor should not produce a difference, but helas, here again the incompatible mix is the culprit.
when constraints and side-effects are involved. Still, I fail to produce such a case just replacing X = Y by Y = X.

Steadfastness: Definition and its relation to logical purity and termination

So far, I have always taken steadfastness in Prolog programs to mean:
If, for a query Q, there is a subterm S, such that there is a term T that makes ?- S=T, Q. succeed although ?- Q, S=T. fails, then one of the predicates invoked by Q is not steadfast.
Intuitively, I thus took steadfastness to mean that we cannot use instantiations to "trick" a predicate into giving solutions that are otherwise not only never given, but rejected. Note the difference for nonterminating programs!
In particular, at least to me, logical-purity always implied steadfastness.
Example. To better understand the notion of steadfastness, consider an almost classical counterexample of this property that is frequently cited when introducing advanced students to operational aspects of Prolog, using a wrong definition of a relation between two integers and their maximum:
integer_integer_maximum(X, Y, Y) :-
Y >= X,
!.
integer_integer_maximum(X, _, X).
A glaring mistake in this—shall we say "wavering"—definition is, of course, that the following query incorrectly succeeds:
?- M = 0, integer_integer_maximum(0, 1, M).
M = 0. % wrong!
whereas exchanging the goals yields the correct answer:
?- integer_integer_maximum(0, 1, M), M = 0.
false.
A good solution of this problem is to rely on pure methods to describe the relation, using for example:
integer_integer_maximum(X, Y, M) :-
M #= max(X, Y).
This works correctly in both cases, and can even be used in more situations:
?- integer_integer_maximum(0, 1, M), M = 0.
false.
?- M = 0, integer_integer_maximum(0, 1, M).
false.
| ?- X in 0..2, Y in 3..4, integer_integer_maximum(X, Y, M).
X in 0..2,
Y in 3..4,
M in 3..4 ? ;
no
Now the paper Coding Guidelines for Prolog by Covington et al., co-authored by the very inventor of the notion, Richard O'Keefe, contains the following section:
5.1 Predicates must be steadfast.
Any decent predicate must be “steadfast,” i.e., must work correctly if its output variable already happens to be instantiated to the output value (O’Keefe 1990).
That is,
?- foo(X), X = x.
and
?- foo(x).
must succeed under exactly the same conditions and have the same side effects.
Failure to do so is only tolerable for auxiliary predicates whose call patterns are
strongly constrained by the main predicates.
Thus, the definition given in the cited paper is considerably stricter than what I stated above.
For example, consider the pure Prolog program:
nat(s(X)) :- nat(X).
nat(0).
Now we are in the following situation:
?- nat(0).
true.
?- nat(X), X = 0.
nontermination
This clearly violates the property of succeeding under exactly the same conditions, because one of the queries no longer succeeds at all.
Hence my question: Should we call the above program not steadfast? Please justify your answer with an explanation of the intention behind steadfastness and its definition in the available literature, its relation to logical-purity as well as relevant termination notions.
In 'The craft of prolog' page 96 Richard O'Keef says 'we call the property of refusing to give wrong answers even when the query has an unexpected form (typically supplying values for what we normally think of as inputs*) steadfastness'
*I am not sure if this should be outputs. i.e. in your query ?- M = 0, integer_integer_maximum(0, 1, M). M = 0. % wrong! M is used as an input but the clause has been designed for it to be an output.
In nat(X), X = 0. we are using X as an output variable not an input variable, but it has not given a wrong answer, as it does not give any answer. So I think under that definition it could be steadfast.
A rule of thumb he gives is 'postpone output unification until after the cut.' Here we have not got a cut, but we still want to postpone the unification.
However I would of thought it would be sensible to have the base case first rather than the recursive case, so that nat(X), X = 0. would initially succeed .. but you would still have other problems..

Prolog compiler return error

I have this hypothetical program to check if a path exists from point A to B.
/*city rules*/
edge(phx, tuc).
edge(tuc, ccg).
edge(ccg, sf).
connected(C1, C2) :-
edge(C1, C2).
connected(C1, C2) :-
edge(C1, X),
connected(X, C2).
the problem is it returns true, then false. Where is the false coming from?
Let's look at the exact reply you got from Prolog! First you got a single true and on pressing SPACE or ; you eventually got:
?- connected(phx,sf).
true
; false.
So you got true ; false. as the complete answer. The ; means "or". So Prolog essentially says: Your query connected(phx,sf). is the same as true ; false. So you need to look at the entire answer to understand what it means. Of course that is a bit odd, when true. would be good enough.
But first let's have another example:
?- connected(phx,X).
X = tuc
; X = ccg
; X = sf
; false.
Here Prolog's complete answer is: connected(phx,X). describes the same set of solutions as X = tuc ; X = ccg ; X = fg ; false. which again would be shorter if false would be omitted.
So why does Prolog write out this false? Prolog computes the answers incrementally. It first shows you X = tuc and waits what you would do. In a sense, it is a bit lazy not to show you everything at once. Sometimes, Prolog does know that there will be no further answer, in that case, it writes a dot directly:
?- member(X,[1,2]).
X = 1
; X = 2.
Here Prolog says: dixi! I have spoken.
But sometimes, it is not that sure:
?- member(1,[1,2]).
true
; false.
After proving that 1 is a member, it stops, otherwise it would have to explore the list further.
So this ; false. means: After the last answer/solution, Prolog was not yet sure that everything has been explored. This might be an inefficiency, it might be an indication that something can be improved. However, never, ever take this as pretext to insert a cut into your program. The cut in another answer is completely malaprop. It is the source for many, many errors. Before starting to use cuts you really should learn the other, pure part of Prolog, first.
BTW: Here is a better definition for connected/2 which avoids infinite loops by using closure/3 (click on it to get the definition).
connected(C0,C) :-
edge(C0,C1)
closure0(edge,C1,C).

Prolog - recursing down family tree

I am trying to write a Prolog program that will print out the male successors of British Royalty in order. My attempt so far:
son(elizabeth, charles).
son(charles, william).
son(charles, henry).
son(elizabeth, andrew).
son(elizabeth, edward).
son(edward, severn).
successor(X, Y) :- son(X, Y).
successor(X, Y) :- son(X, C), successor(C, Y).
The successor function doesn't quite do what I want: the current output is this:
successor(elizabeth, Y).
Y = charles ;
Y = andrew ;
Y = edward ;
Y = william ;
Y = henry ;
Y = severn ;
false.
The first rule makes all three immediate children print out, then the second rule prints out all the descendants. But the descendants of the first child should come before the second immediate child, like this:
successor(elizabeth, Y).
Y = charles ;
Y = william ; % william and henry should come before andrew
Y = henry ;
Y = andrew ;
Y = edward ;
Y = severn ;
false.
This is my first Prolog program, and I am at a loss for how to express the right relationship. Can anyone give me an idea or pointers to resources that would be helpful to me?
As rati noted above, Prolog queries are resolved by choosing a rule, recursively evaluating it using depth-first search, then choosing the next rule and repeating the process. However, the particular rules you're starting with actually result in a breadth-first search of the family tree, which, as you noted, does not give output that matches the actual line of succession. Instead, you want to do a depth-first traversal of the royal family tree. This version gives the result you're looking for:
successor(X, Y) :- son(X, Z), (Y = Z; successor(Z, Y)).
Using this rule, Prolog resolves the query successor(X, Y) roughly as follows:
For each Z who is a son of X:
Bind Y to Z, giving Z as a solution.
The ; operator functions as a logical OR, so now Y is unbound and successor/2 is called recursively to get the successors who are sons of Z.
And yes, please do try to get a copy of the Art of Prolog. It's not the easiest programming book to read, but I found it extremely helpful in my (ongoing) attempt to understand logic programming. There seem to have been some cheap hardcover copies of the 1994 edition floating around eBay lately.
You said:
The first rule makes all three immediate children print out, then the second rule prints out all the descendants.
For any given predicate (such as successor/2), PROLOG will generally evaluate all the possible solutions for the 1st clause, then the next, etc. up to the last clause, in that order. Therefore, PROLOG will behave exactly as you've suggested above - solutions to immediate children will be found first, as the first clause of successor/2 does just that, and the second clause finds the descendants. If you were after a different order, try re-ordering the clauses (i.e.);
successor(X, Y) :- son(X, C), successor(C, Y).
successor(X, Y) :- son(X, Y).
This will cause PROLOG to evaluate to:
?- successor(elizabeth, Y).
Y = william ;
Y = henry ;
Y = severn ;
Y = charles ;
Y = andrew ;
Y = edward.
i.e., all descentants before immediate children.
The ordering you've suggested as wanting, however, can't be achieved through a simple reordering of these subgoals. Instead, consider the various tree traversal methods; i.e., in-order, pre-order and post-order. You could write a (simple) program which is capable of walking the tree structure in various different ways, instead of the default evaluation order for PROLOG. For example, consider the following new definition of successor/2:
successor(Parent, [Son|SonDescendents]) :-
son(Parent, Son),
successor(Son, SonDescendents).
This clause seeks to depth-first populate a list of children under a son, and will backtrack to find all solutions.
successor(NonParent, []) :-
\+ son(NonParent, _).
This next clause takes care of the base-case whereby the given individual does not have any sons, therefore no descendants enter the result list (empty).
Evaluating this gives:
?- successor(elizabeth, S).
S = [charles, william] ;
S = [charles, henry] ;
S = [andrew] ;
S = [edward, severn] ;
false.
ps. I highly recommend the following texts for learning PROLOG:
The Art of Prolog, by Leon Sterling and Ehud Shapiro
The Craft of Prolog, by Richard O'Keefe
Programming in Prolog, by Clocksin and Mellish
Your rule set looks good to me, it's giving you the right results, it's just printing them as it deduces them, which makes the order seem incorrect. Work through the results on paper and you will likely get a similar result.

Resources