prologswi-prologclpfd

swi-prolog abs operator not working in clpfd module


I am doing some toy tests with the CLPFD library in swi-prolog.

Does anybody know why the program below does not work?

start(X,Y):- 
   Vars = [X,Y],
   Vars ins 1..3,
   abs(X-Y) #>= 2,
   X #>= Y,
   nl,
   write([X,Y]), nl.

The expected answer for start(X,Y) would be X=3 and Y=1. However, swi-prolog indicates me multiple answers. The program works properly if I replace

abs(X-Y) #>= 2

by

X-Y #>= 2

My question is whether I am using the abs operator in the right way.


Solution

  • First of all, constraints and side-effects do not flock together. Instead, simply stick to the pure part of your program:

    start(X,Y):- 
       Vars = [X,Y],
       Vars ins 1..3,
       abs(X-Y) #>= 2,
       X #>= Y.
    

    And now, query your relation:

    ?- start(X,Y).
       X in 1..3, X#>=Y, abs(X-Y)#>=2, Y in 1..3.
    

    The answer is a conditional one:

    Yes, there are solutions for X and Y provided all these conditions hold.

    To get actual values, you have to eliminate all these conditions. You have several options:

    In this case, you can use labeling/2:

    ?- start(X,Y), labeling([], [X,Y]).
       X = 3, Y = 1.
    

    So there is exactly one solution. The clpfd-solver alone was not powerful enough to come to this conclusion, it needed some extra help.

    Even better would be to use contracting/1:

    ?- start(X,Y), clpfd:contracting([X,Y]).
       X = 3, Y = 1.
    

    In contrast to labeling, contracting tries to reduce the size of the domain without (visible) search. This makes the solver a bit stronger.

    Reasons why the solver is not powerful enough

    In your case, the abs-constraint admits more solutions!

    ?- [X,Y]ins 1..3, abs(X-Y)#>=2, labeling([],[X,Y]).
       X = 1, Y = 3
    ;  X = 3, Y = 1.
    ?- [X,Y]ins 1..3, X-Y#>=2, labeling([],[X,Y]).
       X = 3, Y = 1.
    

    What you expect is that the extra constraint X #>= Y would help. Alas, the concrete consistency mechanisms are too weak. And not even X #> Y helps:

    ?- [X,Y]ins 1..3, abs(X-Y)#>=2, X#>Y.
       X in 2..3, Y#=<X+ -1, abs(X-Y)#>=2, Y in 1..2.
    

    However, if you switch from SWI to SICStus, things are a bit different:

    | ?- assert(clpfd:full_answer).
         
    | ?- X in 1..3, Y in 1..3, abs(X-Y)#>=2.
         Y+_A#=X, X in 1..3, Y in 1..3, _A in{-2}\/{2}.
    | ?- X in 1..3, Y in 1..3, abs(X-Y)#>=2, X#>Y.
         X = 3, Y = 1.
    

    Please note how abs is resolved!

    And using SICStus with library(clpz) has the same strength:

    | ?- X in 1..3, Y in 1..3, abs(X-Y)#>=2, X#>Y.
         X = 3, Y = 1.
    

    1 Note that I avoid to use the notion of local consistency as opposed to global consistency, since quite often also global consistency only refers to consistency within one "global" constraint.