Which operators and predicates can be used with clp(fd)?

184 Views Asked by At

Firstly, the clp(fd) documentation mentions:

In modern Prolog systems, arithmetic constraints subsume and supersede low-level predicates over integers. The main advantage of arithmetic constraints is that they are true relations and can be used in all directions. For most programs, arithmetic constraints are the only predicates you will ever need from this library.

Secondly, on a previously asked question, it was mentioned that include/3 is incompatible with clp(fd).

Does that mean that only clp(fd) operators and clp(fd) predicates can be used when writing prolog with the clp(fd) library?

Furthermore, for example, why is include/3 incompatible with clp(fd)? Is it because it does not use clp(fd) operators? To use include/3 in clp(fd) code, would one need to rewrite a version that uses clp(fd) operators and constraints?

2

There are 2 best solutions below

12
brebs On BEST ANSWER

include/3 doesn't have to be incompatible with clpfd (or any other use of attributed variables) - depends on the safe (for the purposes of the program, rather than necessarily "logically pure") usage of the attributed variables. E.g.:

:- use_module(library(clpfd)).

test_include(F) :-
    L = [N1, N2, N3, N4],
    N1 #< 2,
    N2 #> 5,
    N3 #< 3,
    N4 #> 8,
    include(gt_3_fd, L, F).

gt_3_fd(I) :-
    I #> 3.

Result in swi-prolog:

?- test_include(F).
F = [_A, _B],
_A in 6..sup,
_B in 9..sup.

The above code is safe, because the variables being used with clpfd are being used consistently with clpfd, and the specified constraints result in reification of gt_3_fd being unnecessary.

Once the variables are nonvar/ground depending on the use-case (clpfd deals with integers, rather than e.g. compound terms, so nonvar is good enough), then the variables can also be used outside of clpfd. Example:

?- I = 5,  I > 4, I @> 4, I #> 4.
I = 5.

An operator such as > uses the actual value of the variable, and ignores any attributes which might have been added to the variable by e.g. clpfd.

Logical purity, e.g. adding constraints to the elements in list L after the include/3 rather than before, is a separate issue, and applies to non-attributed variables also.

In summary: A program may be using some integers with clpfd, and some integers outside of clpfd, i.e. a mixture. That is OK, as long as the inside-or-outside distinction is consistently applied, while it is relevant (because e.g. labeling will produce actual values).

0
false On

why is include/3 incompatible with clp(fd)?

?- X = 1, include(#\=(1),[0,X,2],Xs), X = 1.
   X = 1,
   Xs = [0,2].     % looks good
?-        include(#\=(1),[0,X,2],Xs), X = 1.
   false, unexpected.
?-        include(#\=(1),[0,X,2],Xs).   % generalization
   Xs = [0,X,2],
   X in inf..0\/2..sup
;  unexpected. % missing second answer

So, (#\=)/2 works in this case only if it is sufficiently instantiated. How can you be sure it is? Well, there is no direct safe test. And thus you will get incorrect results in certain cases. As long as these examples fit on a single line, it is rather easy to spot the error. But with a larger program, this is practically impossible. Because of this, constraints and include/3 are incompatible.

A way out would be to produce instantiation errors in cases with insufficient instantiation, but this is pretty hairy in the context of clpfd. Other built-in predicates like (=\=)/2 do this, and are limited in their applicability.

?- X = 1, include(=\=(1),[0,X,2],Xs), X = 1.
   X = 1,
   Xs = [0,2].
?-        include(=\=(1),[0,X,2],Xs), X = 1.
   instantiation_error.    % much better than an incorrect answer

A safe variant of include/3 is tfilter/3 of library(reif). But before using it, I recommend you read this.