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.
Related
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.
let's say I have some predicate a/1, now how would I represent b which is true if a fails for some value ?
Unfortunately not doesn't help here , a definition like this :
b(X):- not(a(X)).
means "b is true if for any X a is false"(I want this to work when X isn't instantiated).
How would someone express this ? and what about the general case where more than one (not instantiated) variable exists ?
Is there more known about a/1?
Many Prolog predicates do have purely relational, sound negations.
For example, the unification X = Y can be cleanly stated not to hold by using the constraint dif/2: dif(X, Y) is true iff X and Y are different. It works correctly in all modes of use.
Similarly, CLP(FD) constraints like (#=)/2, (#>)/2 and others all have a completely sound logical negations. For example, you can say X #\= Y to state that X and Y are distinct integers.
A general way to express such issues is to reify the truth values of your predicates. For example, instead of a predicate a/1, consider a predicate a/2, where the second argument denotes whether the predicate holds in this case. You would call this as a(Arg, Truth), and your job is to implement it in such a way that Truth correctly reflects the truth value of a/1 for Arg. You can throw an instantiation_error in cases where you cannot make a sound decision. The preferable way is of course to declaratively express all possible cases using suitable constraints.
In some cases, constraint refication is already available out of the box. For example, you can negate all reifable CLP(FD) constraints using the predicate (#\)/1. Therefore, #\ (X #= Y) is the same as X #\= Y. Boolean constraints provide similar features.
As pointed before, there is no logical negation in Prolog, since there is no closed universe. Prolog negation is a negation-by-failure. This is, something is false whether it can not be prooved to be true.
In practique, not/1 (or '\+'/1) requieres a ground term to behalf as a logical negation.
You may find some experiments with logical negation (closed universes or domains) in some development environments (as far as I remember, Ciao Prolog has something about that). It requieres variables to be declared as having values at some finite domain.
I was reading a code piece in Prolog and I saw the below code :
r(a).
q(b).
p(X) :- not r(X)
Questions :
?-q(X), p(X) // Result: b
?-p(X), q(X) // Result: no
So, why for ?-q(X), p(X) we get "b" and for ?-p(X), q(X) which is exactly the same as ?-q(X), p(X) we get "no" ?
?-q(X), p(X) // Result: b
q(X) eventually unifies with q(b), which then calls p(b), which calls not r(b), which is true.
?-p(X), q(X) // Result: no
p(X) calls not r(X), which causes X to unify with a, which causes the preceding clause to evaluate to not r(a), which is false, and the conjuction operator short circuits to no.
In fact the two queries are not exactly the same because Prolog "conjunction" operator (the comma) is usually not commutative:
the execution is from left to right and so the order in which the variables are instantiated is different in those queries
if you only use pure logical predicates, that is predicates that are defined not using extra-logical predicates (like not/1 or if-then-else constructs), nor predicates having side-effects (like I/O ones), nor predicates that require some of its arguments to be instantiated beforehand (like is/2), the change in instantiation order does not affect commutativity
but as one of your predicates uses not/1 and it behaves differently when its argument is r(_) or r(b) as explained in C.B. answer, commutativity does not hold.
As you may know not/1 does not implement pure logical negation: it implements so-called negation as failure meaning that it succeeds if the execution of its argument fails and otherwise fails (if the argument is a free variable you get an instantiation error). As the execution is from left to right you cannot expect that its behaviour is affected by some instantiation made after its call.
How would you transform the following prolog statement to predicate logic?
hates(amy, X).
Using LaTeX's \forall to denote the universal quantifier, the meaning of hates(amy,x). is:
\forall x hates(amy,x)
In general, Prolog variables that occur in a program are universally quantified and Prolog variables that occur in a query are existentially quantified. For instance ?- hates(amy,x). would be represented by \exists x hates(amy,x) in FOL.
I've got a very large number of equations which I am trying to use PROLOG to solve. However, I've come a minor cropper in that they are not specified in any sort of useful order- that is, some, if not many variables, are used before they are defined. These are all specified within the same predicate. Can PROLOG cope with the predicates being specified in a random order?
Absolutely... ni (in Italian, Yes and Not)
That is, ideally Prolog requires that you specify what must be computed, not how, writing down the equations controlling the solution in a fairly general logical form, Horn clauses.
But this ideal is far from reach, and this is the point where we, as programmers, play a role. You should try to topologically sort formulae, if you want Prolog just apply arithmetic/algorithms.
But at this point, Prolog is not more useful than any other procedural language. It just make easier to do such topological sort, in sense that formulas can be read (this builtin it's a full Prolog parser!), variables identified and quantified easily, terms transformed, evaluated, and the like (metalanguages features, a strong point of Prolog).
Situation changes if you can use CLP(FD). Just an example, a bidirectional factorial (cool, isn't it?), from the documentation of the shining implementation that Markus Triska developed for SWI-Prolog:
You can also use CLP(FD) constraints as a more declarative alternative for ordinary integer arithmetic with is/2, >/2 etc. For example:
:- use_module(library(clpfd)).
n_factorial(0, 1).
n_factorial(N, F) :- N #> 0, N1 #= N - 1, F #= N * F1, n_factorial(N1, F1).
This predicate can be used in all directions. For example:
?- n_factorial(47, F).
F = 258623241511168180642964355153611979969197632389120000000000 ;
false.
?- n_factorial(N, 1).
N = 0 ;
N = 1 ;
false.
?- n_factorial(N, 3).
false.
To make the predicate terminate if any argument is instantiated, add the (implied) constraint F #\= 0 before the recursive call. Otherwise, the query n_factorial(N, 0) is the only non-terminating case of this kind.
Thus if you write your equations in CLP(FD) you get much more chances to have your 'equation system' solved as is. SWI-Prolog has dedicated debugging for the low level details used to solve CLP(FD).
HTH