I have a little question about the negation as failure in Prolog language:
This is a question more theoretical than practical because I have clear how this example work.
so I have the following Prolog program:
/* Fatti che specificano quali esseri sono degli animali: */
animal(cat).
animal(dog).
animal(frog).
animal(horse).
animal(viper).
animal(boa).
animal(python).
/* Fatti che specificano quali esseri sono dei serpenti: */
snake(viper).
snake(boa).
snake(python).
/* X è un serpente, fallisce ed impedisce il backtracking quindi
il predicato likes(mary,X) risulta essere falso: */
likes(mary,X) :- snake(X),
!,
fail.
/* Se X è un animale allora a mary piace: */
likes(mary, X) :- animal(X).
In Prolog I can't simply say something like: "Mary loves every animals, BUT NOT THE SNAKES"
and I have to formulate it in this way: "If X is a snake, then Mary don't love it. Otherwise, if X it is an animal, mary love it"
The precedent program do exactly this thing, by the rule:
likes(mary,X) :- snake(X),
!,
fail.
Prolog check if it is true that X it is a snake, imposes the cut to avoid backtracking and force a failure of the predicate.
In this way if snake(X) is TRUE the program force the failure also of the head prediate likes(mary,X) and imposing backtracking avoid the possibility to execute the other rule in the program (that answer true because a snake is also an animal)
My question is: it seems me that this use of Prolog falls outside from the logical and declarative paradigm and in some way fall in some sort of procedural paradigm
Because:
I have to impose an order of the 2 predicate (so in some way I am saying: if the first fail, try the second).
But even more I am saying that: if the first rule match (X it is a snake) then execute a forced failure and imposes no backtracking.
This seems to me more near to a procedural meaning that a classical logical meaning...
Is it that? Is it that in these cases, Prolog uses a procedural behavior to overcome a limitation of the logic?
I disagree with 'limitations of the logic'.
The same would be
likes(mary,X) :- not(snake(X)) , animal(X).
Because Prolog uses a depth-first-search some things can be expressed in an shorter way that then depends on the depth-first-search backtracking algorithm.
x :- a, !, b.
x :- c.
x :- d.
is the same as
x :- a, b.
x :- not(a), c.
x :- not(a), d.
Programs that make use of cut (!) are most of the time
sensitive to the ordering of goals and clauses in their
meaning and not only in their termination, so they are
often not declarative.
The negation as failure (\+) ecapsulates in a certain way
the cut. It is defined and even implemented by most Prolog
systems as follows:
\+ X :- X, !, fail.
\+ _ .
Although it hints a logical meaning and thus declarativity,
the negation as failure is still sensitive to ordering of
goals. Here is an example. Assume we have the following
database:
p(a).
q(b,c).
Then the following query produces X=a as a solution:
?- p(X), \+ q(X,Y).
X = a.
But if the arguments of the conjunction (,)/2 switch side,
a different result is obtained:
?- \+ q(X,Y), p(X).
false.
Ergo, negation as failure is not declarative per se. For
ground term flow of arguments, negation as failure sneeks
in an existential quantifiers. So a query of the form:
?- A(X), \+ B(X,Y).
Has essentially the meaning that it quantifies the fresh
variables Y inside the negation:
?- A(X), ~ exists Y B(X,Y).
So in the above example where conjunction is switched, the
set of fresh variables in the negation as failure changes, thats
why different solutions are obtained.
Bye
In short, yes, it is procedural.
Negation as failure uses a cut, and cuts are procedural concepts. You cannot use negation as failure in a declarative way - it is not possible.
It is worth mentioning that not all uses of cuts throw declarativeness out of the window - some of them just increase efficiency. But unfortunately, this is not the case with negation as failure - declarativeness goes out the window.
(Prolog is just a procedural prover disguised as a declarative language lol)
Related
Negation as failure is usually considered impure. The Prolog interpreter needed for negation as failure must realize SLDNF which is an extension of SLD.
The predicate (\=)/2 is for example used in the library(reif). It can be bootstrapped via negation as failure as follows, but is often a built-in:
X \= Y :- \+ X = Y.
Would it be possible to implement (\=)/2 as a pure predicate? Using only pure Prolog, i.e. only first order horn clauses?
Would it be possible to implement (=)/2 as a pure predicate? Using only pure Prolog, i.e. only first order horn clauses?
You can't implement (\=)/2 in pure Prolog.
Proof:
In logic, conjunction is commutative, and pure Prolog queries, if they terminate, must be logical.
However, with (\=)/2, the order of the terms matters, so it is not logical:
?- X \= Y, X=0, Y=1.
false.
?- X=0, Y=1, X \= Y.
X = 0,
Y = 1.
I've read quite a bit about Prolog's Negation by Failure where Prolog in order to prove that \+Goal holds tries to prove that Goal fails.
This is highly connected with CWA (close world assumption) where for example if we query \+P(a) (where P is a predicate of arity 1) and we have no clues that lead to prove P(a) Prolog assumes (due to CWA) that not P(a) holds so \+P(a) succeeds.
From what I've searched this is a way to solve classical logic weakness where if we had no clue about P(a) then we could not answer whether \+P(a) holds.
What described above was the way of introducing non-monotonic reasoning in Prolog. Moreover the interesting part is that Clark proved that Negation by Failure is compatible/similar with classical negation only for ground clauses. I understand that for example:
X=1, \+X==1.: should return false in Prolog (and in classical Logic).
\+X==1, X=1.: should return false in classical logic but it succeeds in Prolog since the time that NF is examined X is not bound, this differs from classic-Pure Logic.
\+X==1.: should not give any answer in classical logic until X is bound, but in Prolog it returns false (possibly to break weakness of classical logic) and this is not same/compatible with pure Logic.
My attempt was to simulate classic negation, thanks to #false's suggestions in comments, current implementation is:
\\+(Goal) :- when(ground(Goal), \+Goal).
Some testing:
?- \\+(X==1).
when(ground(X), \+X==1).
?- X=1, \\+(X==1).
false.
?- \\+(X==1), X=1.
false.
My question:
Is the above a correct interpretation of classical negation?
(Are there any obvious corner cases that it misses?? also I'm concerned about Logic Purity when using when/2, is it safe to assume that the above is pure??).
Prolog cannot do classical negation. Since it does not
use classical inference. Even in the presence of Clark
completion, it cannot detect the following
two classical laws:
Law of noncontradiction: ~(p /\ ~p)
Law of excluded middle: p \/ ~p
Here is an example, take this logic program
and these queries:
p :- p
?- \+(p, \+p)
?- p; \+p
The Clark completion of the logic program is
as follows and the negation as failure query
execution yields the following:
p <-> p
loops
loops
Clark completion adresses the issue of predicate definitions
and negative information. See also section 5.2 Rules and
their Completion. On the other hand, when no predicate
definitions are around, CLP(X) can sometimes do both laws,
when a negation operator is defined deMorgan style. Here is
a negation operator for CLP(B):
?- listing(neg/1).
neg((A;B)) :-
neg(A),
neg(B).
neg((A, _)) :-
neg(A).
neg((_, A)) :-
neg(A).
neg(neg(A)) :-
call(A).
neg(sat(A)) :-
sat(~A).
And here is some execution:
?- sat(P); neg(sat(P)).
P = 0
P = 1.
?- neg((sat(P), neg(sat(P)))).
P = 0
P = 1.
CLP(X) will also have problems when the negation affects domains,
that are usually finite and that would then get infinite. So for
example a constraint such as (#=)/2, ... shouldn't be a problem,
since it can be replaced by a constraint (#\=)/2, ... .
But negation for CLP(FD) will usually not work when applied to constraints
(in)/2. The situation can slightly be mitigated if the CLP(X) system offers
reification. In this case the disjunction can be rendered a little bit more intelligent than just using Prolog backtracking disjunction.
In SWI-Prolog, it is possible to implement the rules of inference for classical logic in Constraint Handling Rules, including de Morgan's laws and the law of noncontradiction:
:- use_module(library(chr)).
:- chr_constraint is_true/1.
:- chr_constraint animal/2.
:- initialization(main).
:- set_prolog_flag('double_quotes','chars').
is_true(A),is_true(A) <=> is_true(A).
is_true(A=B) ==> A=B.
is_true(A\=B) ==> not(A=B).
is_true(not(A)),is_true(A) ==> false.
is_true(not((A;B))) ==> is_true((not(A),not(B))).
is_true(not((A,B))) ==> is_true((not(A);not(B))).
is_true((A,B)) ==> is_true(A),is_true(B).
is_true((A;B)) ==> is_true(A),(is_true(B);is_true(not(B)));is_true(B),(is_true(A);is_true(not(A))).
is_true(not(not(A))) ==> is_true(A).
Then, you can use the solver like this:
is_true(animal(X,A)),is_true(animal((Y,A))) ==> X \= Y,false;X==Y.
is_true((A->B)) ==> is_true(((A;not(A)),B));is_true(((not(A);A),not(B))).
main :- is_true(((X=cat;X=dog;X=moose),(not((animal(dog,tom);animal(moose,tom))),animal(X,tom)))),writeln(animal(X,tom)).
This program prints animal(cat,tom).
But this formula could be solved more efficiently using a different algorithm, such as DPLL.
I am just learning prolog and there is a thing I can't get my head over.
Suppose I have the following program
value(v).
a(X) :- not(value(X)).
So a(v). gives me false, as value(v) can be proved correct.
a(w) gives me true, as there is no fact value(w), therefore, even when trying, it can't be proved correct.
In my understanding, requesting a(X). should give me the first possible value that makes value(X) unproveable. There should be an infinite amount of possibilities, as only value(v) is correct.
But why does Prolog keep answering false?
First of all, please use the ISO predicate (\+)/1 instead of not/1.
Second, please don't use (\+)/1 to denote disequality of terms: (\+)/1 is incomplete in Prolog, and thus not logically sound. It is not logical negation, but rather denotes "not provable".
In your case: ?- value(X). succeeds, so it is provable, so ?- \+ value(X). fails although there are instantiations that make the query succeed.
In particular, ?- \+ value(a). succeeds.
So we have:
?- \+ value(V).
false.
But a more specific query succeeds:
?- V = a, \+ value(V).
V = a.
This obviously runs counter to logical properties we expect from pure relations. See logical-purity.
To denote disequality of terms, use dif/2. If your Prolog system does not support dif/2, ask for its inclusion, or use iso_dif/2 as a safe approximation that is logically sound. See prolog-dif for more information.
Prolog operates under "closed world assumption" – it only knows what we told it about. In particular, we've told it nothing about no w, u, or any other stuff, so how could it produce them to us? And why should w come before u, and not vice versa?
The only thing sensible could be to produce (X, dif(X,v)), but it would be the answer to a different question, namely, "how to make a(X) provable?", not the one Prolog is actually answering, namely "is a(X) provable?".
To ease up your cognitive burden, rename the Prolog prompt's replies in your head from true to Yes, and from false to No.
Yes would mean Prolog telling us "yes, I could prove it!", and No – "no, I couldn't prove it."
Also rename "not" to read \+ as not_provable, mentally.
Say I have the following theory:
a(X) :- \+ b(X).
b(X) :- \+ c(X).
c(a).
It simply says true, which is of course correct, a(X) is true because there is no b(X) (with negation as finite failure). Since there is only a b(X) if there is no c(X) and we have c(a), one can state this is true. I was wondering however why Prolog does not provide the answer X = a? Say for instance I introduce some semantics:
noOrphan(X) :- \+ orphan(X).
orphan(X) :- \+ parent(_,X).
parent(david,michael).
Of course if I query noOrphan(michael), this will result in true and noOrphan(david) in false (since I didn't define a parent for david)., but I was wondering why there is no proactive way of detecting which persons (michael, david,...) belong to the noOrphan/1 relation?
This probably is a result of the backtracking mechanism of Prolog, but Prolog could maintain a state which validates if one is searching in the positive way (0,2,4,...) negations deep, or the negative way (1,3,5,...) negations deep.
Let's start with something simpler. Say \+ X = Y. Here, the negated goal is a predefined built-in predicate. So things are even clearer: X and Y should be different. However, \+ X = Y fails, because X = Y succeeds. So no trace is left under which precise condition the goal failed.
Thus, \+ \+ X = Y does produce an empty answer, and not the expected X = Y. See this answer for more.
Given that such simple queries already show problems, you cannot expect too much of user defined goals such as yours.
In the general case, you would have to first reconsider what you actually mean by negation. The answer is much more complex than it seems at first glance. Think of the program p :- \+ p. should p succeed or fail? Should p be true or not? There are actually two models here which no longer fits into Prolog's view of going with the minimal model. Considerations as these opened new branches to Logic Programming like Answer Set Programming (ASP).
But let's stick to Prolog. Negation can only be used in very restricted contexts, such as when the goal is sufficiently instantiated and the definition is stratified. Unfortunately, there are no generally accepted criteria for the safe execution of a negated goal. We could wait until the goal is variable free (ground), but this means quite often that we have to wait way too long - in jargon: the negated goal flounders.
So effectively, general negation does not go very well together with pure Prolog programs. The heart of Prolog really is the pure, monotonic subset of the language. Within the constraint part of Prolog (or its respective extensions) negation might work quite well, though.
I might be misunderstanding the question, and I don't understand the last paragraph.
Anyway, there is a perfectly valid way of detecting which people are not orphans. In your example, you have forgotten to tell the computer something that you know, namely:
person(michael).
person(david).
% and a few more
person(anna).
person(emilia).
not_orphan(X) :- \+ orphan(X).
orphan(X) :- person(X), \+ parent(_, X).
parent(david, michael).
parent(anna, david).
?- orphan(X).
X = anna ;
X = emilia.
?- not_orphan(X).
X = michael ;
X = david ;
false.
I don't know how exactly you want to define an "orphan", as this definition is definitely a bit weird, but that's not the point.
In conclusion: you can't expect Prolog to know that michael and david and all others are people unless you state it explicitly. You also need to state explicitly that orphan or not_orphan are relationships that only apply to people. The world you are modeling could also have:
furniture(red_sofa).
furniture(kitchen_table).
abstract_concept(love).
emotion(disbelief).
and you need a way of leaving those out of your family affairs.
I hope that helps.
I am trying to understand why Prolog implementations do not behave according to the execution model in textbooks -- for example, the one in the book by Sterling and Shapiro's "The Art of Prolog" (chapter 6, "Pure Prolog", section 6.1, "The Execution Model of Prolog").
The execution model to which I refer is this (page 93 of Sterling & Shapiro):
Input: A goal G and a program P
Output: An instance of G that is a logical consequence of P, or no otherwise
Algorithm:
Initialize resolvent to the goal G
while resolvent not empty:
choose goal A from resolvent
choose renamed clause A' <- B_1, ..., B_n from P
such that A, A' unify with mgu θ
(if no such goal and clause exist, exit the "while" loop)
replace A by B_1, ..., B_n in resolvent
apply θ to resolvent and to G
If resolvent empty, then output G, else output NO
Additionally (page 120 of the same book), Prolog chooses goals (choose goal A) in left-to-right order, and searches clauses (choose renamed clause ...) in the order they show up in the program.
The program below has a definition of not (called n in the program) and one single fact.
n(X) :- X, !, fail.
n(X).
f(a).
If I try to prove n(n(f(X))), it succeeds (according to two textbooks and also on SWI Prolog, GNU Prolog and Yap). But isn't this a bit strange? According to that execution model, which several books expose, this is what I would expect to happen (skipping renaming of variables to keep things simple, since there would be no conflict anyway):
RESOLVENT: n(n(f(Z)))
unification matches X in first clause with n(f(Z)), and replaces the goal with the tail of that clause.
RESOLVENT: n(f(Z)), !, fail.
unification matches again X in the first clause with f(Z), and replaces the first goal in the resolvent with the tail of the clause
RESOLVENT: f(Z), !, fail, !, fail.
unification matches f(Z) -> success! Now this is eliminated from the resolvent.
RESOLVENT: !, fail, !, fail.
And "!, fail, !, fail" should not succeed! After the cut there is a fail. End of story. (And indeed, entering !,fail,!,fail as a query will fail in all Prolog systems that I have access to).
So may I presume that the execution model in textbooks is not precisely what Prolog uses?
edit: changing the first clause to n(X) :- call(X), !, fail makes no difference in all Prologs I tried.
Your program is not a pure Prolog program, since it contains a !/0 in n/1. You may ask yourself the simpler question: With your definitions, why does the query ?- n(f(X)). fail although there clearly is a fact n(X) in your program, meaning that n(X) is true for every X, and should therefore hold in particular for f(X) as well? This is because the program's clauses can no longer be considered in isolation due to the usage of !/0, and the execution model for pure Prolog cannot be used. A more modern and pure alternative for such impure predicates are often constraints, for example dif/2, with which you can constrain a variable to be distinct from a term.
The caption below does tell you what this particular algorithm is about:
Figure 4.2 An abstract interpreter for logic programs
Also, its description reads:
Output: An instance of G that is a logical consequence of P, or no otherwise.
That is, the algorithm in 4.2 only shows you how to compute a logical consequence for logic programs. It only gives you an idea for how Prolog actually works. And in particular cannot explain the !. Also, the algorithm in 4.2 is only able to explain how one solution ("consequence") is found, but Prolog tries to find all of them in a systematic manner called chronological backtracking. The cut interferes with chronological backtracking in a very particular manner which cannot be explained at the level of this algorithm.
You wrote:
Additionally (page 120 of the same book), Prolog chooses goals (choose goal A) in left-to-right order, and searches clauses (choose renamed clause ...) in the order they show up in the program.
That misses one important point which you can read on page 120:
Prolog's execution mechanism is obtained from the abstract interpreter by choosing the leftmost goal ... and replacing the non-deterministic choice of a clause by sequential search for a unifiable clause and backtracking.
So it is this little addition "and backtracking" which makes things more complex. You cannot see this in the abstract algorithm.
Here is a tiny example to show that backtracking is not explicitly handled in the algorithm.
p :-
q(X),
r(X).
q(1).
q(2).
r(2).
We would start with p which is rewritten to q(X), r(X) (there is no other way to continue).
Then, q(X) is selected, and θ = {X = 1}. So we have r(1) as the resolvent. But now, we do not have any matching clause, so we "exit the while loop" and answer no.
But wait, there is a solution! So how do we get it? When q(X) was selected, there was also another option for θ, i.e. θ = {X = 2}. The algorithm itself is not explicit about the mechanism to perform this operation. It only says: If you make the right choice everywhere, you will find an answer. To get a real algorithm out of that abstract one, we thus need some mechanism to do this.
When you reach the last step:
RESOLVENT: !, fail, !, fail
the cut ! here means, "erase everything". So the resolvent becomes empty. (this is faking it of course, but is close enough). cuts have no meaning at all here, the first fail says to flip the decision, and 2nd fail to flip it back. Now resolvent is empty - the decision was "YES", and remains so, twice flipped. (this is also faking it ... the "flipping" only makes sense in the presence of backtracking).
You can't of course place a cut ! on the list of goals in the resolvent, as it is not just one of the goals to fulfill. It has an operational meaning, it normally says "stop trying other choices" but this interpreter keeps no track of any choices (it "as if" makes all the choices at once). fail is not just a goal to fulfill too, it says "where you've succeeded say that you didn't, and vice versa".
So may I presume that the execution model in textbooks is not precisely what Prolog uses?
yes of course, the real Prologs have cut and fail unlike the abstract interpreter that you referred to. That interpreter has no explicit backtracking and instead has multiple successes by magic (its choice is inherently non-deterministic as if all the choices are made at once, in parallel - real Prologs only emulate that through sequential execution with explicit backtracking, to which the cut is referring - it simply has no meaning otherwise).
I think you got it almost right. The problem is here:
RESOLVENT: !, fail, !, fail.
The first ! and fail are from the second time that the first clause was matched. The other two are from the first time.
RESOLVENT: ![2], fail[2], ![1], fail[1].
The cut and fail have effect on the clause that is being processed -- NOT on the clause that "called" it. If you work through the steps again, but using these annotations, you'll get the right result.
![2], fail[2] makes the second call to n fail without backtracking. But the other call (the first) can still backtrack -- and it will:
RESOLVENT: n(_)
And the result is "yes".
This shows that Prolog keeps information about backtracking using a stack discipline. You may be interested in the the virtual machine that is used as a model for Prolog implementations. It is quite more complex than the execution model you mentioned, but the translation of Prolog into the VM will give you a much more accurate understanding of how Prolog works. This is the Warren Abstract Machine (WAM). The tutorial by Hasan Aït-Kaci is the best explanation you'll find for it (and it explains the cut, which if I remember correctly was absent from the original WAM description). If you are not used to abstract theoretical texts, you may try reading the text by Peter van Roy first: "1983-1993: the wonder years of sequential Prolog implementation". This article is clear and basically goes through the history of Prolog implementations, but giving special attention to the WAM. However, it does not show how the cut is implemented. If you carefully read it, however, you may be able to pick up Hasan's tutorial and read the section in which he implements the cut.
You have an extra level of nesting in your test goal:
n(n(f(X))
instead of:
n(f(X))
And indeed, if we try that, it works as expected:
$ prolog
GNU Prolog 1.3.0
By Daniel Diaz
Copyright (C) 1999-2007 Daniel Diaz
| ?- [user].
compiling user for byte code...
n(X) :- call(X), !, fail.
n(_X).
f(a).
user compiled, 4 lines read - 484 bytes written, 30441 ms
yes
| ?- f(a).
yes
| ?- n(f(a)).
no
| ?- n(f(42)).
yes
| ?- n(n(f(X))).
yes
| ?- n(f(X)).
no
| ?- halt.
So your understanding of Prolog is correct, your test case was not!
Updated
Showing the effects of negations of negations:
$ prolog
GNU Prolog 1.3.0
By Daniel Diaz
Copyright (C) 1999-2007 Daniel Diaz
| ?- [user].
compiling user for byte code...
n(X) :- format( "Resolving n/1 with ~q\n", [X] ), call(X), !, fail.
n(_X).
f(a) :- format( "Resolving f(a)\n", [] ).
user compiled, 4 lines read - 2504 bytes written, 42137 ms
(4 ms) yes
| ?- n(f(a)).
Resolving n/1 with f(a)
Resolving f(a)
no
| ?- n(n(f(a))).
Resolving n/1 with n(f(a))
Resolving n/1 with f(a)
Resolving f(a)
yes
| ?- n(n(n(f(a)))).
Resolving n/1 with n(n(f(a)))
Resolving n/1 with n(f(a))
Resolving n/1 with f(a)
Resolving f(a)
no
| ?- n(n(n(n(f(a))))).
Resolving n/1 with n(n(n(f(a))))
Resolving n/1 with n(n(f(a)))
Resolving n/1 with n(f(a))
Resolving n/1 with f(a)
Resolving f(a)
yes
| ?- halt.
While mat is right in that your program is not pure prolog (and this is relevant as the title of the chapter is Pure Prolog), not only since you use a cut but also since you write predicates that handle other predicates (pure prolog is a subset of first order logic) this is not the main issue; you are just missing backtracking
While you indeed have a cut, this will not be reached until goal n(f(X)) succeeds. However, as you know, this will fail and therefore prolog will backtrack and match the second clause.
I do not see how that would contradict with the model described in 6.1 (and would find it hard to believe that other books would describe a model where the execution would continue after failing and thus allow for the cut to prune the other solutions). In any case, I find that jumping to the conclusion that "Prolog implementations do no behave according to the execution model in textbooks" is quite similar to "there is a bug to the compiler", especially since the "counter-example" behaves as it should (not(not(true)) should be true)