5

I'm playing around with constraints in (swi) prolog using the clpfd library.

I'm trying to identify when one set of constraints encapsulates or subsumes the other, e.g. X<4 encapsulates X<7 as whenever the former is true, the latter is true. This can be easily represented using logical implication. However, I couldn't get the #==> operator to give me the result I wanted, so I resorted to using not(Co1 #/\ #\Co2) where Co1 and Co2 are constraints. This is fine for individual constraints, but I then wanted to pass a conjunctions of constraints into Co1 and Co2.

Now here is the rub. When I try

X#<7 #/\ #\X#<4.

I get back

X in 4..6,
X+1#=_G822,
X+1#=_G834,
_G822 in 5..7,
_G834 in 5..7.

(oddly enough, doing this in Sicstus results in a segmentation fault)

When I pass in

X#<7,X#<4

I get the desired

X in inf..3.

Obviously, I can't pass the latter into not(Co1 #/\ #\Co2), but the former doesn't give me the result I want. Can anyone explain why the two approaches yield different results, and how I can get the former to act like the latter?

false
  • 10,182
  • 12
  • 93
  • 182
Nir
  • 61
  • 6

2 Answers2

4

Subsumption of general arithmetic constraints over the integers is undecidable in general, so all correct solvers have inherent limits beyond which they have to delay their answers until more is known. If you know your domains to be finite, you can post the domains and then try to find counterexamples that would make the implication invalid, using the constraint solver's labeling/2 predicate. Consider also that linear inequalities over Q are decidable, and that SWI-Prolog's library(clpq) is complete for them. You can thus try your constraints in CLP(Q) with:

?- use_module(library(clpq)).
true.

?- { X < 4, X >= 7 }.
false.

and see that no such counterexample exists in Q (hence also not in Z), and thus the implication holds.

false
  • 10,182
  • 12
  • 93
  • 182
mat
  • 39,707
  • 3
  • 42
  • 68
  • Many thanks, I am dealing with linear inequalities. I'm trying to automatically find ranges for a set of conjunctive (possibly negated) constraints. Thus, I'd like to be able to pass in (for example) X#<4,\#(X#>2), which works. I'd also like to pass in something more complex, e.g. X#<4,#\\(X#>2,X#<1), which doesn't work, as the #\ is then treated as a binary operator. Similarly, giving it X#<4,#\\((X#>2,X#<1)) also results in an error. – Nir Nov 06 '10 at 11:18
  • 1
    To negate a conjunction, you have to use #/\, for example: #\ (A #/\ B). – mat Feb 01 '11 at 18:31
2

It seems you are dealing with CLP(FD). These solvers distinguish the setup phase and the labeling phase of solving a constraint problem.

A CLP(FD) solver does not completely reduce a problem during the setup phase. Since it has the chance to reduce variable ranges during the labeling phase. Thus it could be that during setup a problem is posed which could be reduced by other solvers to "No", but it will not with a CLP(FD) solver. Only during labeling a "No" will be detected.

How much reduction is done during the setup phase highly depends on the given CLP(FD) system. Some CLP(FD) systems do more reduction during the setup phase, while other do less. GNU Prolog for example uses some indexical propagation, whereas SWI Prolog does not. So we find for example, not your example:

SWI-Prolog:

?- X #< Y, Y #< Z, Z #< X.
Z#=<X+ -1,
X#=<Y+ -1,
Y#=<Z+ -1.

GNU Prolog:

?- X #< Y, Y #< Z, Z #< X.
(7842 ms) no

Further since you are using reified constraints it also depends a little bit how clever the reification is done. But I guess in the present case its only a matter of propagation. We find now for your example:

SWI-Prolog:

?- X #< 4 #==> X #< 7.
X+1#=_G1330,
X+1#=_G1342,
7#>=_G1330#<==>_G1354,
_G1354 in 0..1,
_G1377#==>_G1354,
_G1377 in 0..1,
4#>=_G1342#<==>_G1377.

GNU Prolog:

?- X #< 4 #==> X #< 7.
X = _#22(0..268435455)

There is a tradeoff between doing more reduction in the setup phase and leaving more work to the labeling phase. And the whole matter also depends on the given example. But when you have also labeling beside setup, you will not see any difference in terms of outcome:

SWI-Prolog:

?- X in 0..9, X #< 4 #==> X #< 7, label([X]).
X = 0 ;
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
X = 5 ;
X = 6 ;
X = 7 ;
X = 8 ;
X = 9.

GNU Prolog:

?- fd_domain(X,0,9), X #< 4 #==> X #< 7, fd_labeling([X]).
X = 0 ? ;
X = 1 ? ;
X = 2 ? ;
X = 3 ? ;
X = 4 ? ;
X = 5 ? ;
X = 6 ? ;
X = 7 ? ;
X = 8 ? ;
X = 9

I didn't test with SICStus Prolog or B-Prolog. But I guess they will behave somewhere in the vincinity of SWI-Prolog and GNU Prolog.

CLP(Q) is no real alternative if your domain is really FD, since it will miss some "No" reductions, which CLP(FD) would not miss. For example the following is unsatisfiable in CLP(FD), but satisfiable in CLP(Q):

X = Y + 1, Y < Z, Z < X.

Bye

Mostowski Collapse
  • 12,118
  • 3
  • 34
  • 78