Prolog: difference between var, nonvar and ground - prolog

In Prolog, especially in it's metaprogramming aspects, people often talk about ground and non-ground variables. As well as using predicates such as var/1, nonvar/1 and ground/1. But what exactly is the distincion between them?
My current understanding is the following:
A var is completely uninstantiated (eg. X)
A nonvar is instantiated, but might contain some variables deeper down (eg. term(1,2,Y)). This is similar to a weak head normal form from Haskell.
A ground var is completely instantiated, all the way down (eg. term(1,2,3)).
Is this correct?

Nearly.
If var(X) then variable X designates something that is uninstantiated, a "hole". X is a "fresh variable". Note: That predicate should really be named fresh(...). Whether X is a variable is actually a question about the program text. But what we want to know is whether what is in between the parentheses is a fresh variable (at the moment that call is made, because, quite non-logically, that can change.)
nonvar(X) is just the complement of var(X), same as \+ var(X). Whatever is between the parentheses designates something (if it is a variable) or is something (if it is a non-variable term, as in nonvar(foo)) that is not a "hole".
ground(X) means that whatever is between the parenthese designates something or is something that has no holes in its structure (in effect, no holes at the term's leaves).
Some test code. I expected the compiler to issue more warnings than it did.
:- begin_tests(var_nonvar).
% Amazingly, the compiler does not warn about the code below.
test("var(duh) is always false", fail) :-
var(duh).
% Amazingly, the compiler does not warn about the code below.
test("var(X) is true if X is a fresh variable (X designates a 'hole')") :-
var(_).
% Compiler warning: " Singleton variable, Test is always true: var(X)"
test("var(X) is true if X is a fresh variable (X designates a 'hole')") :-
var(X).
% The hole designated by X is filled with f(_), which has its own hole.
% the result is nonvar (and also nonground)
test("var(X) maybe true but become false as computation progresses") :-
var(X),X=f(_),nonvar(X).
test("var(X) is false otherwise") :-
var(_).
% The hole is designated by an anonymous variable
test("a fresh variable is not ground, it designates a 'hole'", fail) :-
ground(_).
% Both hhe holes are designated by anonymous variables
test("a structure with 'holes' at the leaves is non-ground", fail) :-
ground(f(_,_)).
test("a structure with no 'holes' is ground") :-
ground(f(x,y)).
test("a structure with no 'holes' is ground, take 2") :-
X=f(x,y), ground(X).
% var/1 or ground/1 are questions about the state of computation,
% not about any problem in logic that one models. For example:
test("a structure that is non-ground can be filled as computation progresses") :-
K=f(X,Y), \+ ground(f(X,Y)), X=x, Y=y, ground(f(X,Y)).
:- end_tests(var_nonvar).

Related

Why does this give a NonGroundProbabilisticClause error, but not the deterministic counterpart?

I want the program to represent that if person 1 uses one out of a particular list of substances then there is an 80% chance that person uses nicotine.
As in the example below, I have defined substance/1 to be true only for a few atoms.
What explains the difference in behavior between the two versions of property/3? The probabilistic version does not seem to have the issues discussed in Problogs FAQs.
substance(methadone).
substance(heroin).
P::property(X,use,nicotine) :- %gives NonGroundProbabilisticClause error
property(X,use,Z),
substance(Z),
P is 0.8.
property(X,use,nicotine) :- %appropriately infers nicotine use
property(X,use,Z),
substance(Z).
person(1).
substance(Y).
property(1, use, Y).
query(property(1,use, nicotine)).
Update
Discussion with #2bigpips brings out a related issue. Assigning a probability at the head of a probabilistic clause doesn't mean that you are clamping the probability at that value. This is important to realize if you are linking the probabilities to estimated quantities, for example, from experiments.
Short answer: Deterministic clauses and probabilistic clauses have different semantics. Long answer in part 2.
Part 1: What your program means
substance(Y). % means everything is a substance.
property(1, use, Y). % means: 1 uses everything (whatever 1 is)
As indicated in Paulo's answer to your previous question, the scope of the variable is limited to the clause. The Y in these facts are not the same variable. From a logic perspective, you have 2 separate clauses:
(\forall Y: substance(Y))
(\forall Z: person(1, use, Z))
His suggestion will work and means person 1 uses all substances.
substance(methadone).
substance(heroin).
substance(methadone).
substance(heroin).
0.8::property(X,use,nicotine) :- %gives NonGroundProbabilisticClause error
property(X,use,Z),
substance(Z).
% person(1).
% property(1, use, Y):- substance(Y). % person1 uses all substances
query(property(1,use, nicotine)).
% property(1,use,nicotine): 0.96
EDIT after seeing what you meant
Since you want it to be 0.8 whether uses 1 or many substances, you can just do this:
argument-in-prolog-using-a-series-of-facts
substance(methadone).
substance(heroin).
0.8::property(X,use,nicotine) :-
uses_atleast_one_substance(X).
uses_atleast_one_substance(X):-
property(X,use,Z),
substance(Z).
person(1).
property(1, use, Y):- substance(Y).
query(property(1,use, nicotine)).
Part 2: What's happening
I have to make a guess here: The non-probabilistic clause may work because it's non-probabilistic and hence not an annotated-disjunction. It may have prolog-like semantics.
Probabilistic clauses follow the semantics of annotated disjunctions. Every probabilistic clause is implicitly an annotated disjunction -
p::head:- body. is p::head ; (1-p)not_head:- body.
Here's a paper with the semantics of "Logic Programs with Annotated Disjunctions".
It says every grounding of the body independently causes the head to be true with the specified probability.
With that in mind, The reason you're getting the error is because there is indeed a non-ground BODY in the proof.
P::property(X,use,nicotine) :- %gives NonGroundProbabilisticClause error
property(X,use,W), % This can unify with person(1,use,Z), leaving Z free
substance(Z), % This can unify with substance(Y), leaving Z free
P is 0.8.
You can't have variables in the body because you don't know how many causes there are. It breaks the semantics and problog detects it and throws an error. That's what's happening.

What am I missing about equality and unification in Prolog?

I'm working through Clocksin and Mellish to try and finally go beyond just dabbling in Prolog. FWIW, I'm running SWI-Prolog:
SWI-Prolog version 7.2.3 for x86_64-linux
Anyway, I implemented a diff/2 predicate as part of exercise 1.4. The predicate is very simple:
diff(X,Y) :- X \== Y.
And it works when used in the sister_of predicate, like this:
sister_of(X,Y) :-
female(X),
diff(X,Y),
parents(X, Mum, Dad ),
parents(Y, Mum, Dad ).
in that, assuming the necessary additional facts, doing this:
?- sister_of(alice,alice).
returns false as expected. But here's the rub. If I do this instead:
?- sister_of(alice, Who).
(again, given the additional facts necessary)
I get
Who = edward ;
Who = alice;
false
Even though, as already shown, the sister_of predicate does not treat alice as her own sister.
On the other hand, if I use the SWI provided dif/2 predicate, then everything works the way I would naively expect.
Can anyone explain why this is happening this way, and why my diff implementation doesn't work the way I'm expecting, in the case where I ask for additional unifications from that query?
The entire source file I'm working with can be found here
Any help is much appreciated.
As you note, the problem stems from the interplay between equality (or rather, inequality) and unification. Observe that in your definition of sister_of, you first find a candidate value for X, then try to constrain Y to be different, but Y is still an uninstantiated logic variable and the check is always going to succeed, like diff(alice, Y) will. The following constraints, including the last one that gives a concrete value to Y, come too late.
In general, what you need to do is ensure that by the time you get to the inequality check all variables are instantiated. Negation is a non-logical feature of Prolog and therefore potentially dangerous, but checking whether two ground terms are not equal is safe.

Detecting whether proving a predicate unified something

I have a predicate that may unify its arguments, for example:
foo(X) :- X = 42.
How can I tell if, while proving foo(X), unification changed X? For example, I would like to know if writeln(X), foo(X), writeln(X) would print the same value for X twice, without actually doing the printing.
My actual implementation of foo/1 is actually much more complex, so please don't suggest specific to the simplified version above. In my program, foo(X) simplifies X using unification, but foo(X) may need to be proven several times until all simplifications have been performed. I would like to be able to write a foohelper(X) predicate that invokes foo(X) until X stops being unified.
Assuming we have only syntactic unification - that is, no constraints:
:- meta_predicate(call_instantiated(0,?)).
call_instantiated(Goal_0, Instantiated) :-
copy_term(Goal_0, Copy_0),
Goal_0,
( subsumes_term(Goal_0, Copy_0) -> % succeeds iff equal u.t.r.
Instantiated = false
; Instantiated = true
).
Note that Goal_0 will or will not be further instantiated. The above subsumes_term/2 tests whether or not Goal_0 is now "more general" than Copy_0. Of course, it cannot be more general, so effectively that test tests whether or not the terms are identical up to renaming of variables.
Compared to using term_variables/2, as #PauloMoura has indicated, this may more may not be more efficient. It primarily depends on the efficiency of subsumes_term/2.
Maybe you can use the standard term_variables/2 predicate? You can call it with your goal before and after calling the goal and check if the returned lists of variables are different. Something like:
...,
term_variables(foo(X), Vars0),
foo(X),
term_variables(foo(X), Vars),
( Vars0 == Vars ->
write(simplified)
; write(not_simplified)
),
...

Make a predicate reversible

I'm new to prolog; I'm coming from a structured programming background, as will become obvious :)
I am building up a prolog query that involves reversing a number; eg. reverse_num(123,X) results in X = 321. I came up with the following definition, but it only works when I provide a number as the first parameter.
reverse_num(Num, Revnum) :-
number_chars(Num, Atoms),
reverse(Revatoms, Atoms),
number_chars(Reversed, Revatoms),
Reversed = Revnum.
the number_chars/2 predicate doesn't like an unsubstantiated variable if I do: reverse_num(X,123) (where I'm expecting X to be 321).
Am I trying too hard to make reverse_num do something it shouldn't (should it be understood to work only with a number as the first parameter and variable as the second)?
Or is there an easy / straight-forward way to handle a variable as the first parameter?
Relational naming
Before jumping into coding, let's take a step back. After all, the idea in Prolog is to define relations. Your name reverse_num/2 rather suggests some actions, num_reversed/2 might be a better name.
Determine the relation
Your definition is not that bad, let me rewrite it to1:
num_reversed(Num, Reversed) :-
number_chars(Num, Chars),
reverse(Chars, Revchars),
number_chars(Reversed, Revchars).
?- num_reversed(123,X).
X = 321.
?- num_reversed(1230,X).
X = 321.
?- num_reversed(12300,X).
X = 321.
Do you see the pattern? All numbers N*10^I have the same result!
Now, let's ask some more:
?- num_reversed(Num, 321).
error(instantiation_error,number_chars/2).
Hm, what did we expect? Actually, we wanted all 123*10^I to be printed. That's infinitely many solutions. So above query, if correctly answered, would require infinitely many solutions to be printed. If we print them directly, that will take all our universe's lifetime, and more!
It is for this reason, that Prolog produces an instantiation error instead. By this, Prolog essentially states:
This goal is too general that I can make a good answer. Maybe there are infinitely many solutions, maybe not. I know not. But at least I indicate this by issuing an error. To remove this error you need to instantiate the arguments a bit more.
So the answer Prolog produced was not that bad at all! In fact, it is much better to produce a clean error than to, say, fail incorrectly. In general, Prolog's errors are often a very useful hint to what semantic problems you might have. See all error classes how.
Coroutining
As have other answers suggested, coroutining, using when/2 might solve this problem. However, coroutining itself has many semantic problems. Not without reason, systems like XSB do not offer it, due to the many problems related to subsumption checking. An implementation that would be compatible to it would be unexpectedly inefficient.
But for the sake of the point, we could make our definition more versatile by querying it like
?- when(nonvar(Num), num_reversed(Num, Reversed)).
when(nonvar(Num), num_reversed(Num, Reversed)).
Now we get back as an answer exactly the query we entered. This is also known as floundering. So there is a way to represent infinitely may solutions in a compact manner! However, this comes at a rather high price: You no longer know whether a solution exists or not. Think of:
?- when(nonvar(Num), num_reversed(Num, -1)).
when(nonvar(Num), num_reversed(Num, -1)).
Others have suggested to wait also for nonvar(Reversed) which would only be correct if we would produce infinitely many answers - but, as we have seen - this just takes too much time.
Coroutining looked as a very promising road at the beginning of the 1980s. However, it has never really caught on as a general programming methodology. Most of the time you get much too much floundering which is just a pain and even more difficult to handle than, say instantiation errors.
However, a more promising offspring of this development are constraints. There, the mechanisms are much cleaner defined. For practical purposes, programmers will only use existing libraries, like CLPFD, CLPQ, or CHR. Implementing your own library is an extremely non-trivial project in its own right. In fact it might even be possible to provide an implementation of num_reversed/2 using library(clpfd) that is, restricting the relation to the integer case.
Mode dependent conditionals
Traditionally, many such problems are solved by testing for instantiations explicitly. It is good style to perform this exclusively with nonvar/1 and ground/1 like the condition in when/2- other type test predicates lead easily to errors as exemplified by another answer.
num_reversed(Num, Reversed) :-
( nonvar(Num)
-> original_num_reversed(Num, Reversed)
; original_num_reversed(Reversed, Base),
( Base =:= 0
-> Num is 0
; length(_, I),
Num is Base*10^I
)
).
Above code breaks very soon for floats using base 2 and somewhat later for base 10. In fact, with classical base 2 floats, the relation itself does not make much sense.
As for the definition of number_chars/2, ISO/IEC 13211-1:1995 has the following template and mode subclause:
8.16.7.2 Template and modes
number_chars(+number, ?character_list)
number_chars(-number, +character_list)
The first case is when the first argument is instantiated (thus nonvar). The second case, when the first argument is not instantiated. In that case, the second argument has to be instantiated.
Note, however, that due to very similar problems, number_chars/2 is not a relation. As example, Chs = ['0','0'], number_chars(0, Chs) succeeds, whereas number_chars(0, Chs), Chs = ['0','0'] fails.
Very fine print
1 This rewrite is necessary, because in many Prologs reverse/2 only terminates if the first argument is known. And in SWI this rewrite is necessary due to some idiosyncratic inefficiencies.
The number_chars/2 predicate has the signature:
number_chars(?Number, ?CharList)
But although not fully specified by the signature, at least Number or CharList have to be instantiated. That's where the error occurs from.
If you call:
reverse_num(Num,123)
You will call number_chars/2 with both uninstatiated at that time so the predicate will error.
A not very nice solution to the problem is to ask whether Num or RevNum are number/2s. You can do this by writing two versions. It will furthermore filter other calls like reverse_num(f(a),b), etc.:
reverse_num(Num,Revnum) :-
\+ number(Num),
\+ number(Revnum),
throw(error(instantiation_error, _)).
reverse_num(Num, Revnum) :-
ground(Num),
number(Num),
!,
number_chars(Num, Atoms),
reverse(Revatoms, Atoms),
number_chars(Revnum, Revatoms).
reverse_num(Num, Revnum) :-
ground(Revnum),
number(Revnum),
reverse_num(Revnum,Num).
Or you can in case you use two nongrounds (e.g. reverse_num(X,Y).) an instantiation error instead of false as #false says:
reverse_num(Num,Revnum) :-
\+ number(Num),
\+ number(Revnum),
!,
throw(error(instantiation_error, _)).
reverse_num(Num, Revnum) :-
number(Num),
!,
number_chars(Num, Atoms),
reverse(Revatoms, Atoms),
number_chars(Revnum, Revatoms).
reverse_num(Num, Revnum) :-
reverse_num(Revnum,Num).
The cut (!) is not behaviorally necessary, but will increase performance a bit. I'm not really a fan of this implementation, but Prolog cannot always fully make predicates reversible since (a) reversibility is an undecidable property because Prolog is Turing complete; and (b) one of the characteristics of Prolog is that the body atoms are evaluated left-to-right. otherwise it will take ages to evaluate some programs. There are logic engines that can do this in an arbitrary order and thus will succeed for this task.
If the predicate/2 is commutative, a solution that can be generalized is the following pattern:
predicate(X,Y) :-
predicate1(X,A),
predicate2(A,B),
% ...
predicaten(C,Y).
predicate(X,Y) :-
predicate(Y,X).
But you cannot simply add the last clause to the theory, because it can loop infinitely.
Nice to see someone is also worried about define flexible rules with no restrictions in the set of bound arguments.
If using a Prolog system that supports coroutining and the when/2 built-in predicate (e.g. SICStus Prolog, SWI-Prolog, or YAP), try as:
reverse_num(Num, Reversed) :-
when( ( ground(Num); ground(Atoms) ), number_chars(Num, Atoms) ),
when( ( ground(Reversed); ground(Revatoms) ), number_chars(Reversed, Revatoms) ),
reverse(Atoms , Revatoms).
that gives:
?- reverse_num( 123, X ).
X = 321.
?- reverse_num( X, 123 ).
X = 321 .
( thanks to persons who provided theses answers: Prolog: missing feature? )
This SWISH session shows my effort to answer.
Then I've come back here, where I found I was on #PasabaPorAqui' mood (+1), but I didn't get it right.
But, such an interesting topic: notice how regular is the join pattern.
reverse_num(X, Y) :-
when((nonvar(Xs);nonvar(Ys)), reverse(Xs, Ys)),
when((nonvar(X) ;nonvar(Xs)), atomic_chars(X, Xs)),
when((nonvar(Y) ;nonvar(Ys)), atomic_chars(Y, Ys)).
So, we can generalize in a simple way (after accounting for PasabaPorAqui correction, ground/1 it's the key):
% generalized... thanks Pasaba Por Aqui
:- meta_predicate when_2(0).
when_2(P) :-
strip_module(P,_,Q),
Q =.. [_,A0,A1],
when((ground(A0);ground(A1)), P).
reverse_num(X, Y) :-
maplist(when_2, [reverse(Xs, Ys), atomic_chars(X, Xs), atomic_chars(Y, Ys)]).
I think I understand why nonvar/1 was problematic: the list bound for reverse get 'fired' too early, when just the head get bound... too fast !
maplist/2 is not really necessary: by hand we can write
reverse_num(X, Y) :-
when_2(reverse(Xs, Ys)),
when_2(atomic_chars(X, Xs)),
when_2(atomic_chars(Y, Ys)).
this seems an ideal application of term rewriting... what do you think about -:- ? Implementing that we could write bidirectional code like
reverse_num(X, Y) -:-
reverse(Xs, Ys),
atomic_chars(X, Xs),
atomic_chars(Y, Ys).
edit SWISH maybe is not 'term_rewrite' friendly... so here is a lower level approach:
:- op(900, xfy, ++).
A ++ B ++ C :- when_2(A), B ++ C.
A ++ B :- when_2(A), when_2(B).
reverse_num(X, Y) :-
reverse(Xs, Ys) ++ atomic_chars(X, Xs) ++ atomic_chars(Y, Ys).
Setting aside the problem of trailing zeroes turning into leading zeroes, it doesn't seem like it should be much more complicated than something like this (made somewhat more complicated by dealing with negative numbers):
reverse_number(X,Y) :- number(X) , ! , rev(X,Y) .
reverse_number(X,Y) :- number(Y) , ! , rev(Y,X) .
rev(N,R) :-
N < 0 ,
! ,
A is abs(N) ,
rev(A,T) ,
R is - T
.
rev(N,R) :-
number_chars(N,Ns) ,
reverse(Ns,Rs) ,
number_chars(R,Rs)
.
Note that this does require at least one of the arguments to reverse_number/2 to be instantiated.

Safer type tests in Prolog

ISO-Prolog (ISO/IEC 13211-1:1995 including Cor.1:2007, Cor.2:2012) offers the following built-in predicates for testing the type of a term:
8.3 Type testing
1 var/1. 2 atom/1. 3 integer/1. 4 float/1. 5 atomic/1. 6 compound/1. 7 nonvar/1. 8 number/1. 9 callable/1. 10 ground/1. 11 acyclic_term/1.
Within this group there are those whose purpose is solely to test for a certain instantiation, that is 8.3.1 var/1, 8.3.7 nonvar/1, 8.3.10 ground/1, and those that assume that a term is sufficiently instantiated such that the type test is safe. Unfortunately, they are combined with testing for a concrete instantiation.
Consider the goal integer(X) which fails if X is a nonvar term that is not an integer and when X is a variable. This destroys many desirable declarative properties:
?- X = 1, integer(X).
true.
?- integer(X), X = 1.
false.
Ideally the second query would either succeed using some form of coroutining ; or it would issue an instantiation error1 according to the error classification. After all:
7.12.2 Error classification
Errors are classified according to the form of Error_term:
a) There shall be an Instantiation Error when an
argument or one of its components is a variable, and an
instantiated argument or component is required. It has
the form instantiation_error.
...
Note that this implicit combination of instantiation testing and type testing leads to many errors in Prolog programs and also here on SO.
A quick fix to this situation would be to add an explicit test in front of every test built-in, either verbosely as
( nonvar(T) -> true ; throw(error(instantiation_error,_)) ),
integer(T), ....
or more compactly as
functor(T, _,_),
integer(T), ....
it could be even
T =.. _,
integer(T), ...
My question is twofold:
How to provide this functionality on the user level?
and, to make this also a bit challenging:
What is the most compact implementation of a safer atomic/1 written in ISO-Prolog?
1 Other less desirable options would be to loop or to produce a resource error. Still preferable to an incorrect result.
The testing for types needs to distinguish itself from the traditional "type testing" built-ins that implicitly also test for a sufficient instantiation. So we effectively test only for sufficiently instantiated terms (si). And if they are not sufficiently instantiated, an appropriate error is issued.
For a type nn, there is thus a type testing predicate nn_si/1 with the only error condition
a) If there is a θ and σ such that nn_si(Xθ) is
true and nn_si(Xσ) is false —
instantiation_error.
atom_si(A) :-
functor(A, _, 0), % for the instantiation error
atom(A).
integer_si(I) :-
functor(I, _, 0),
integer(I).
atomic_si(AC) :-
functor(AC,_,0).
list_si(L) :-
\+ \+ length(L, _), % for silent failure
sort(L, _). % for the instantiation error
This is available as library(si) in Scryer.
In SWI, due to its differing behavior in length/2, use rather:
list_si(L) :-
'$skip_list'(_, L, T),
functor(T,_,_),
T == [].
This is a very naive attempt at implementing both your suggested solutions.
First, has_type(Type, Var) that succeeds, or fails with an instantiation error:
has_type(Type, X) :-
var(X), !,
throw(error(instantiation_error, _)).
has_type(Type, X) :-
nonvar_has_type(Type, X).
nonvar_has_type(atom, X) :- atom(X).
nonvar_has_type(integer, X) :- integer(X).
nonvar_has_type(compound, X) :- compound(X).
% etc
Second, a could_be(Type, Var) (analogy to must_be/2) that uses coroutining to allow the query to succeed at some point in the future:
could_be(Type, X) :-
var(X), !,
freeze_type(Type, X).
could_be(Type, X) :-
nonvar_has_type(Type, X).
freeze_type(integer, X) :- freeze(X, integer(X)).
freeze_type(atom, X) :- freeze(X, atom(X)).
freeze_type(compound, X) :- freeze(X, compound(X)).
% etc
There are several weak points to this approach but your comments might help me understand the use cases better.
EDIT: On "types" in Prolog
Types in Prolog, as I understand them, are not "types": they are just information that can be queried at run time, and which exists because it is a useful leaky abstraction of the underlying implementation.
The only way I have been able to make practical use of a "type" has been to "tag" my variables, as in the compound terms number(1), number(pi), operator(+), date(2015, 1, 8), and so on. I can then put variables in there, write deterministic or semi-deterministic predicates, understand what my code means when I see it a week later....
So a free variable and an integer are just terms; mostly because, as your question very smartly points out, a free variable can become an integer, or an atom, or a compound term. You could use coroutining to make sure that a free variable can only become a certain "type" of term later, but this is still inferior to using compound terms, from a practical point of view.
It highly likely that I am confounding very different issues here; and to be honest, my experience with Prolog is limited at best. I just read the documentation of the implementation I am using, and try to find out the best way to use it to my advantage.

Resources