Datalog Stratification - prolog

So I'm trying to understand how Datalog works and one of the differences between it and Prolog is that it has stratification limitations placed upon negation and recursion.
To quote Wikipedia:
If a predicate P is positively derived from a predicate Q (i.e., P is
the head of a rule, and Q occurs positively in the body of the same
rule), then the stratification number of P must be greater than or
equal to the stratification number of Q
If a predicate P is derived from a negated predicate Q (i.e., P is the
head of a rule, and Q occurs negatively in the body of the same rule),
then the stratification number of P must be greater than the
stratification number of Q,
So, going by this, the two following predicates do not result in a stratification error as they can simply be assigned the same stratification number. So these predicates are fine, despite the circular definition.
A(x) :- B(x)
B(x) :- A(x)
But contrast that with what happens if we have a definition which has some negation involved (Where ~ is negation)
A(x) :- ~ B(x)
B(x) :- ~ A(x)
Here a stratification is impossible. A(x,y) must have a stratification number greater than B(x,y), and B(x,y) must have a stratification number greater than A(x,y). My first thought was that this was not okay because this is a circular definition, but stratification is fine with circularity so long as the predicates are not negated. But why? Truth values are simply binary. It seems extremely arbitrary to treat formulas which have a negation symbol differently in this manner. What is this stratification trying to prevent in the second case which isn't in the first?

I think the problem with:
A(x) :- \+ B(x)
B(x) :- \+ A(x)
...is that it has ambiguous semantics. This program has two minimal models, namely, {A(x)} and {B(x)}, and is therefore not well-defined under the fixed point semantics (no fixed point) or under the model theoretic semantics (no unique minimal model).
In order to address this problem, stratified semantics for Datalog imposes restrictions on the syntax of Datalog programs such that, if a stratification exists for the program, then it will also have a unique, minimal model in both the fixed point and model theoretic semantics (and vice-versa, I believe).
You can find more on the precise details of stratified semantics for Datalog in the text "Foundations of Databases by Serge Abiteboul, Richard Hull, and Victor Vianu" which happens to be freely available online, with the relevant detail in Chapter 15. This excellent text also explains most of the other terms I've used above like model, fixed-point, etc. if you're stuck.

Related

Does the Prolog symbol :- mean Implies, Entails or Proves?

In Prolog we can write very simple programs like this:
mammal(dog).
mammal(cat).
animal(X) :- mammal(X).
The last line uses the symbol :- which informally lets us read the final fact as: if X is a mammal then it is also an animal.
I am beginning to learn Prolog and trying to establish which of the following is meant by the symbol :-
Implies (⇒)
Entails (⊨)
Provable (⊢)
In addition, I am not clear on the difference between these three. I am trying to read threads like this one, but the discussion is at a level above my capability, https://math.stackexchange.com/questions/286077/implies-rightarrow-vs-entails-models-vs-provable-vdash.
My thinking:
Prolog works by pattern-matching symbols (unification and search) and so we might be tempted to say the symbol :- means 'syntactic entailment'. However this would only be true of queries that are proven to be true as a result of that syntactic process.
The symbol :- is used to create a database of facts, and therefore is semantic in nature. That means it could be one of Implies (⇒) or Entails (⊨) but I don't know which.
Neither. Or, rather if at all, then it's the implication. The other symbols are above, that is meta-language. The Mathematics Stack Exchange answers explain this quite nicely.
So why :- is not that much of an implication, consider:
p :- p.
In logic, both truth values make this a valid sentence. But in Prolog we stick to the minimal model. So p is false. Prolog uses a subset of predicate logic such that there actually is only one minimal model. And worse, Prolog's actual default execution strategy makes this an infinite loop.
Nevertheless, the most intuitive way to read LHS :- RHS. is to see it as a way to generate new knowledge. Provided RHS is true it follows that also LHS is true. This way one avoids all the paradoxa related to implication.
The direction right-to-left is a bit counter intuitive. This direction is motivated by Prolog's actual execution strategy (which goes left-to-right in this representation).
:- is usually read as if, so something like:
a :- b, c .
reads as
| a is true if b and c are true.
In formal logic, the above would be written as
| a ← b ∧ c
Or
| b and c imply a

Prolog - proof tree misses possibilities

I have the following Prolog Program:
p(f(X), Y) :- p(g(X), g(Y)).
p(g(X), Y) :- p(f(Y), f(X)).
p(f(a), g(b)).
The prolog proof tree has to be drawn for the predicate p(X, Y).
Question:
Why is Y matched to Y1/Y and not to Y/Y1 and why is Y used further on?
if I match a predicate (e.g. p(X, Y)), I get a new predicate (e.g. p(g(X1), g(Y))) - why contains p(g(X1), g(Y)) just one subtree? I mean, shouldn't it have 3 because the knowledgebase contains 3 statements - instead of just 1?
And why is at each layer of the tree matched with something like X2/X1 and so on ? and not with the predicate before ?
Shouldn't it be g(X1)/fX5, g(Y1)/Y5 ?
Note: Maybe it seems that I have never done a tutorial or something. But I did.. I appreciate every help.
To be honest, I have rarely seen a worse method to explain Prolog than what you show here.
Yes, I expect the author meant Y/Y1 instead of Y1/Y in both cases, otherwise the notation would be quite inconsistent.
As to your other questions: You are facing the usual problems that arise when taking such an extremely operational view of Prolog. The core issue is that this method doesn't scale: You do not have the mental capacity to carry this approach through. Don't take this personal: Humans in general are bad at keeping all details of an execution tree that grows exponentially in mind. This makes the whole approach extremely cumbersome and error-prone. For comparison, consider why human grandmasters have stopped competing against chess computers already many years ago. In this concrete case, note for example that the rightmost branch does not even arise in actual Prolog execution, but the graph wrongly suggests that it does!
Part of the problem here is a confusion in terminology: Please note that Prolog uses unification (not "matching", which is one-sided unification). When you unify a goal with a clause head and the unification succeeds, then you get bindings for variables. You continue with these bindings in place.
To make the whole approach remotely feasible, consider fragments of your program.
For example, suppose I only give you the following fact:
p(f(a), g(b)).
And you then query:
?- p(X, Y).
X = f(a),
Y = g(b).
This answers shows the bindings for X and Y. First make sure you understand this, and understand the difference between these bindings and a "new predicate" (which does not arise!).
Also, there are no "statements", but 3 clauses, which are logical alternatives.
Now, again to simplify the whole task, consider the following fragment of your program, in which I only look at the two rules:
p(f(X), Y) :- p(g(X), g(Y)).
p(g(X), Y) :- p(f(Y), f(X)).
Already with this program, we get:
?- p(X, Y).
nontermination
Adding a further pure clause cannot prevent this nontermination. Thus, I recommend you start with this reduced version of your program, and consider it in more depth.
From there, you can add the remaining fact again, and consider the differences.
Very good questions!
Why is Y matched to Y1/Y and not to Y/Y1 and why is Y used further on?
The naming here seems a little arbitrary in that they could have used Y/Y1 but then would need to use Y1 further on. In this case, they chose Y1/Y and use Y further on. Although the author of this expression tree was inconsistent in their convention, I wouldn't be too concerned about the naming as much as whether they follow the variable correctly down the tree.
if I match a predicate (e.g. p(X, Y)), I get a new predicate (e.g. p(g(X1), g(Y))) - why contains p(g(X1), g(Y)) just one subtree? I mean, should'nt it have 3 because the knowledgebase contains 3 statements - instead of just 1?
First a word on term versus predicate. A term is only a predicate in the context of Head :- Body in which case Head is a term that forms the head of a predicate clause. If a term is an argument to a predicate (for example, p(g(X1), g(Y)), the g(X1) and g(Y) are not predicates. They are just terms.
More specifically in this case, the term p(g(X1), g(Y)) only has one subtree because it only matches the head of one of the 3 predicate clauses which is the one with the head p(g(X), Y) (it matches with X = X1 and Y = g(Y)). The other two can't match since they're of the form p(f(...), ...) and the f(...) term cannot match the g(X1) term.
And why is at each layer of the tree matched with something like X2/X1 and so on ? and not with the predicate before ?
Shouldn't it be g(X1)/fX5, g(Y1)/Y5 ?
I'm not sure I'm following this question, but the principle to follow is that the tree is attempting to use the same variable name if it applies to the same variable in memory, whereas a different variable name (e.g., X1 versus X) is used if it's a different X. For example, if I have foo(X, Y) :- <some code>, bar(f(X), Y). and I have bar(X, Y) :- blah(X), ... then the X referred to in the bar predicate is different than the X referred to in the foo predicate. So we might say, in the call to foo(X, Y) we're calling bar(f(X), Y), or alternatively, bar(X1, Y) where X1 = f(X).

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.

Difference between two variant implementations

Is there any logical difference between these two implementations of a variant predicate?
variant1(X,Y) :-
subsumes_term(X,Y),
subsumes_term(Y,X).
variant2(X_,Y_) :-
copy_term(X_,X),
copy_term(Y_,Y),
numbervars(X, 0, N),
numbervars(Y, 0, N),
X == Y.
Neither variant1/2 nor variant2/2 implement a test for being a syntactic variant. But for different reasons.
The goal variant1(f(X,Y),f(Y,X)) should succeed but fails. For some cases where the same variable appears on both sides, variant1/2 does not behave as expected. To fix this, use:
variant1a(X, Y) :-
copy_term(Y, YC),
subsumes_term(X, YC),
subsumes_term(YC, X).
The goal variant2(f('$VAR'(0),_),f(_,'$VAR'(0))) should fail but succeeds. Clearly, variant2/2 assumes that no '$VAR'/1 occur in its arguments.
ISO/IEC 13211-1:1995 defines variants as follows:
7.1.6.1 Variants of a term
Two terms are variants if there is a bijection s of the
variables of the former to the variables of the latter such that
the latter term results from replacing each variable X in the
former by Xs.
NOTES
1 For example, f(A, B, A) is a variant of f(X, Y, X),
g(A, B) is a variant of g(_, _), and P+Q is a variant of
P+Q.
2 The concept of a variant is required when defining bagof/3
(8.10.2) and setof/3 (8.10.3).
Note that the Xs above is not a variable name but rather (X)s. So s is here a bijection, which is a special case of a substitution.
Here, all examples refer to typical usages in bagof/3 and setof/3 where variables happen to be always disjoint, but the more subtle case is when there are common variables.
In logic programming, the usual definition is rather:
V is a variant of T iff there exists σ and θ such that
Vσ and T are identical
Tθ and V are identical
In other words, they are variants if both match each other. However, the notion of matching is pretty alien to Prolog programmers, that is, the notion of matching as used in formal logic. Here is a case which lets many Prolog programmers panic:
Consider f(X) and f(g(X)). Does f(g(X)) match f(X) or not? Many Prolog programmers will now shrug their shoulders and mumble something about the occurs-check. But this is entirely unrelated to the occurs-check. They match, yes, because
f(X){ X ↦ g(X) } is identical to f(g(X)).
Note that this substitution replaces all X and substitutes them for g(X). How can this happen? In fact, it cannot happen with Prolog's typical term representation as a graph in memory. In Prolog the node X is a real address somehow in memory, and you cannot do such an operation at all. But in logic things are on an entirely textual level. It's just like
sed 's/\<X\>/g(X)/g'
except that one can also replace variables simultaneously. Think of { X ↦ Y, Y ↦ X}. They have to be replaced at once, otherwise f(X,Y) would shrink into f(X,X) or f(Y,Y).
So this definition, while formally perfect, relies on notions that have no direct correspondence in Prolog systems.
Similar problems happen when one-sided unification is considered which is not matching, but the common case between unification and matching.
According to ISO/IEC 13211-1:1995 Cor.2:2012 (draft):
8.2.4 subsumes_term/2
This built-in predicate provides a test for syntactic one-sided unification.
8.2.4.1 Description
subsumes_term(General, Specific) is true iff there is a
substitution θ such
that
a) Generalθ
and Specificθ are identical, and
b) Specificθ and Specific
are identical.
Procedurally, subsumes_term(General, Specific) simply
succeeds or fails accordingly. There is no side effect or
unification.
For your definition of variant1/2, subsumes_term(f(X,Y),f(Y,X)) already fails.

Why double negation doesn't bind in Prolog

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.

Resources