Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog - arguments are not instantiated

Tags:

prolog

clpfd

I'm trying to understand how Prolog works. I'm using SWI-Prolog. Here's a bit of code:

forall(C1,C2) :- \+ (C1, \+ C2).

foo(N) :- N < 10.

bar(N) :- N > 5.

foobar(N) :- forall(foo(N),bar(N)).

It produces desired output if I do something like:

?- foobar(5).
false.

But when I try to see all the possible values I get an error:

?- foobar(N).
ERROR: </2: Arguments are not sufficiently instantiated

What is going on here?

like image 941
akalikin Avatar asked Mar 18 '15 19:03

akalikin


1 Answers

Basically you are writing a program the check for a global implication:

forall N, if N < 10 then N > 5

and you want to know for which domain of N that holds.

Now you correctly rewrite that in prolog as:

\+ ( N < 10, \+ ( N > 5 ) ).

But if you try to give that query to the prolog interpreter, it will output the error:

?- \+ ( N < 10, \+ ( N > 5 ) ).
ERROR: </2: Arguments are not sufficiently instantiated

because the arguments of < are not instantiated. It happens the same thing with a simple query such as N < 3. Of course, if you instantiate it before the query, the problem goes away:

?- N=5, \+ ( N < 10, \+ ( N > 5 ) ).
false.

(the statement does not hold for N=5)

?- N=6, \+ ( N < 10, \+ ( N > 5 ) ).
N = 6.

(but it holds for N=6).

You could also put a predicate that generates multiple assignments for N via backtracking, in place of that N=6:

?- between(1, 12, N), \+ ( N < 10, \+ ( N > 5 ) ).
N = 6 ;
N = 7 ;
N = 8 ;
N = 9 ;
N = 10 ;
N = 11 ;
N = 12.

but for a large domain, that is going to be highly inefficient (it will require backtracking for every element of the domain).

If you want to reason about finite domains (i.e. integers), you can use the CLPFD library as @lurker suggested. This approach is more efficient, because it has rules for reasoning about intervals, intersection on intervals, and many more.

You have to replace <, >, ,, \+ with the CLP operators #<, #>, #/\, #\. Let's try it:

?- use_module(library(clpfd)).
?- #\ ( N #< 10 #/\ #\ ( N #> 5 ) ).
N+1#=_G11193,
N#>=6#<==>_G11205,
10#>=_G11193#<==>_G11217,
_G11217 in 0..1,
_G11217#/\_G11244#<==>0,
_G11244 in 0..1,
#\_G11205#<==>_G11244,
_G11205 in 0..1.

that might be a bit hard to read, but among many other things, it is telling you the answer you are looking for, that is the domain of N: N #>= 6.

like image 105
fferri Avatar answered Sep 30 '22 01:09

fferri