Seeking a pure Prolog implementation of (\=)/2 - prolog

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.

Related

How does unify predicate (=)/2 differ from first order equality?

The tag logical purity mentions (=)/2 as pure. Is it
"intrinsically" pure or "operational" pure? To the
best of my knowledge it can be defined by this Horn clause:
∀x(=(x, x))
Which is this Prolog fact, if were not already a built-in:
X = X.
This means (=)/2 would be "intrinsically" pure, as SWI-Prolog already remarks. So what is
then the difference to first order equality (FOL=), if there
are any differences?
The "intrinsinc" definition of (=)/2 I guess does assure that the unify predicate is reflexive, symmetric and transitive. Requirements that are also satisfied by FOL=. But FOL= also requires congruence, which is this axiom schema:
/* Predicate Congruence in FOL= */
∀x1..∀xn∀yj(=(xj, yj) & p(x1, .., xn) -> p(x1, .., xj-1, yj, xj+1, .., xn))
Which is not represented by the only Horn clause for the unify predicate. And since the unify predicate is a built-in, the missing Horn clauses can also not be added. So what can go wrong? Can we make an example that would go wrong?
The classical example is this fact:
p(morning_star).
Obviously this query succeeds in Prolog:
?- p(morning_star).
true
But this query fails in Prolog, whereas in FOL= it would succeed:
?- morning_star = evening_star, p(evening_star).
false
The situation is different in so called unification modulo theories, where the unification and also the unify predicate might change their meaning.

what's the semantics of Prolog's implementation of negation with non-ground literals

I am familiar with Prolog's implementation of negation as NaF and that even its implementation of NaF is incomplete, esp. floundering with non ground negated literals. My question here is regarding the specific semantics. Suppose you have a clause p(X) :- q(Y). This is the clausal form of \A x,y(q(y) -> p(x)) which is \E y q(y) -> \A x p(x), and this is indeed the semantics that prolog implements. But now consider if I have p(X) :- \+ q(Y). In FOL this would be expressed as \E y ~q(y) -> \A x p(x) ie "if q fails for some y then p holds for every x" but this does not appear to be the semantics that Prolog implements. Rather Prolog will require q to finitely fail for every y before \+ q(y) succeeds and p is true for any x. So its semantics appears to be very different, not just incomplete. Am I missing something?
thanks
In Prolog, p(X) :- \+ q(Y). on its own doesn't sufficiently express the notion that p holds true for every X if q fails for some Y because Prolog doesn't know the possible universe of Y values that are eligible to test q. So, it doesn't know of any values of Y for which q(Y) is not provable.
Let's suppose you had, though, the following:
q(a).
q(b).
q(c).
valid_y(Y) :- member(Y, [a,b,c,d,e]).
Then you could write:
p(_) :- valid_y(Y), \+ q(Y).
Then p(_) succeeds twice as Prolog seeks all solutions. You could use a cut or once/1 to avoid that.

Logical Negation in Prolog

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.

prolog doesn't give me a solution when one exists

I am working through Seven Languages in Seven Weeks, but there is something I don't understand about prolog. I have the following program (based on their wallace and grommit program):
/* teams.pl */
onTeam(a, aTeam).
onTeam(b, aTeam).
onTeam(b, superTeam).
onTeam(c, superTeam).
teamMate(X, Y) :- \+(X = Y), onTeam(X, Z), onTeam(Y, Z).
and load it like this
?- ['teams.pl'].
true.
but it doesn't give me any solutions to the following
?- teamMate(a, X).
false.
it can solve simpler stuff (which is shown in the book):
?- onTeam(b, X).
X = aTeam ;
X = superTeam.
and there are solutions:
?- teamMate(a, b).
true ;
false.
What am I missing? I have tried with both gnu prolog and swipl.
...AND THERE IS MORE...
when you move the "can't be your own teammate" restriction to then end:
/* teams.pl */
onTeam(a, aTeam).
onTeam(b, aTeam).
onTeam(b, superTeam).
onTeam(c, superTeam).
teamMate(X, Y) :- onTeam(X, Z), onTeam(Y, Z), \+(X = Y).
it gives me the solutions I would expect:
?- ['teams.pl'].
true.
?- teamMate(a, X).
X = b.
?- teamMate(b, X).
X = a ;
X = c.
What gives?
You have made a very good observation! In fact, the situation is even worse, because even the most general query fails:
?- teamMate(X, Y).
false.
Declaratively, this means "there are no solutions whatsoever", which is obviously wrong and not how we expect relations to behave: If there are solutions, then more general queries must not fail.
The reason you get this strange and logically incorrect behaviour is that (\+)/1 is only sound if its arguments are sufficiently instantiated.
To express disequality of terms in a more general way, which works correctly no matter if the arguments are instantiated or not, use dif/2, or, if your Prolog system does not provide it, the safe approximation iso_dif/2 which you can find in the prolog-dif tag.
For example, in your case (note_that_I_am_using_underscores_for_readability instead of tuckingTheNamesTogetherWhichMakesThemHarderToRead):
team_mate(X, Y) :- dif(X, Y), on_team(X, Z), on_team(Y, Z).
Your query now works exactly as expected:
?- team_mate(a, X).
X = b.
The most general query of course also works correctly:
?- team_mate(X, Y).
X = a,
Y = b ;
X = b,
Y = a ;
X = b,
Y = c ;
etc.
Thus, using dif/2 to express disequality preserves logical-purity of your relations: The system now no longer simply says false even though there are solutions. Instead, you get the answer you expect! Note that, in contrast to before, this also works no matter where you place the call!
The answer by mat gives you some high-level considerations and a solution. My answer is a more about the underlying reasons, which might or might not be interesting to you.
(By the way, while learning Prolog, I asked pretty much the same question and got a very similar answer by the same user. Great.)
The proof tree
You have a question:
Are two players team mates?
To get an answer from Prolog, you formulate a query:
?- team_mate(X, Y).
where both X and Y can be free variables or bound.
Based on your database of predicates (facts and rules), Prolog tries to find a proof and gives you solutions. Prolog searches for a proof by doing a depth-first traversal of a proof tree.
In your first implementation, \+ (X = Y) comes before anything else, so it at the root node of the tree, and will be evaluated before the following goals. And if either X or Y is a free variable, X = Y must succeed, which means that \+ (X = Y) must fail. So the query must fail.
On the other hand, if either X or Y is a free variable, dif(X, Y) will succeed, but a later attempt to unify them with each other must fail. At that point, Prolog will have to look for a proof down another branch of the proof tree, if there are any left.
(With the proof tree in mind, try to figure out a way of implementing dif/2: do you think it is possible without either a) adding some kind of state to the arguments of dif/2 or b) changing the resolution strategy?)
And finally, if you put \+ (X = Y) at the very end, and take care that both X and Y are ground by the time it is evaluated, then the unification becomes more like a simple comparison, and it can fail, so that the negation can succeed.

Negation as failure in Prolog is a procedural behavior?

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)

Resources