bagof/3 is unpredictable

169 Views Asked by At

I am baffled by the following results. I am using SWI-Prolog.

?- bagof(Q, (Q=A, (A=[a,_] ; A=[_,b])), X).
A = [_G16898, b],
X = [[_G16898, b]] ;
A = [a, _G16892],
X = [[a, _G16892]].

Notice that [a,_] and [_,b] are not unified to produce an answer A = [a,b], X=[[a,b],[a,b]].

Now, lets try the same with arithmetic constraints:

?- bagof(Q, (Q=A, (A in 1..5 ; A in 3..8)), X).
X = [A, A],
A in 3..5.

Strangely, this time the arithmetic constraints are taken together but there are no answers A in 1..5, X=[A] and A in 3..8, X=[A].

Now lets try this in yet another way:

?- bagof(Q, (Q=A, ((1 #=< A, A #=< 5) ; (3 #=< A, A #=< 8))), X).
X = [A],
A in 3..5 ;
X = [A],
A in 3..5.

The arithmetic constraints are combined like before, but we have two answers instead of one.

How can all this be explained?

EDIT: Some more strange results. Compare this:

?- A=[_,_], bagof(Q, K1^K2^(Q=A, (A=[a,K1] ; A=[K2,b])), X).
A = [_G16886, b],
X = [[_G16886, b]] ;
A = [a, _G16889],
X = [[a, _G16889]].

with this:

?- A=[a,b], bagof(Q, K1^K2^(Q=A, (A=[a,K1] ; A=[K2,b])), X).
A = [a, b],
X = [[a, b], [a, b]].
1

There are 1 best solutions below

0
AudioBubble On

Thats an artefact of SWI-Prolog, which also does copy attributed variables while taking findall/3 copies. findall/3 copies are used inside bagof/3 before doing keysort/2. But the effect can already be explained by means of findall/3:

SWI-Prolog, findall/3 copies attributed variables conditions:

?- findall(A, A in 1..5, L).
L = [_3464],
_3464 in 1..5.

?- findall(A, (A in 1..5, (true; true)), L).
L = [_3762, _3768],
_3762 in 1..5,
_3768 in 1..5

Jekejeke Prolog, findall/3 does not copy attributed variables conditions:

?- findall(A, A in 1..5, L).
L = [_A]

?- findall(A, (A in 1..5, (true; true)), L).
L = [_A, _B]

Inside bagof/3, there is not only a keysort/2 step, but also step where variables are restored. In this step for SWI-Prolog, constraints might be joined, since constraints will be present.

This explains the first result in the question of the OP. The second result in the question of the OP can be explained in that SWI-Prolog does goal expansion and introduce new variables in the case of (#=<)/2. You can check yourself:

?- [user].
test(A) :- A in 1..5 ; A in 3..8.
test(A) :- (1 #=< A, A #=< 5) ; (3 #=< A, A #=< 8).

?- listing(test/1).
test(A) :-
    (   (   integer(A)
        ->  between(1, 5, A)
        ;   clpfd:clpfd_in(A, 1..5)
        )
    ;   integer(A)
    ->  between(3, 8, A)
    ;   clpfd:clpfd_in(A, 3..8)
    ).
test(A) :-
    (   (   integer(A)
        ->  A>=1
        ;   B=1,
            clpfd:clpfd_geq(A, B)
        ),
        (   integer(A)
        ->  5>=A
        ;   C=5,
            clpfd:clpfd_geq(C, A)
        )
    ;   (   integer(A)
        ->  A>=3
        ;   D=3,
            clpfd:clpfd_geq(A, D)
        ),
        (   integer(A)
        ->  8>=A
        ;   E=8,
            clpfd:clpfd_geq(E, A)
        )
    ).

There are no fresh variables in the expansion of (in)/2. But I guess the fresh variables inside the (#=<)/2 expansion, then causes bagof/3 to see two solutions, instead of only one.

Edit 19.08.2019:
Now I wonder how tabling with CLP(FD) solves the problem...