1

I am trying to craete a custom clpfd global constraint, which is very similar to the all_diferent constraint.

The constraint should allow only lists with different non-zero items (so there can be multiple zeros).

Here is a working example in SICSTUS Prolog:

:- use_module(library(clpfd)).

mask([], [], []).
mask([H1|T1], [H2|T2], [P|R]) :- 
    P #= H1 * H2, mask(T1, T2, R).

diff(L) :-
    diff_rec(L, L).

diff_rec([], _).
diff_rec([H|T], L) :-
    fd_global(diff_zero(H, T, L), no_state, [val(H)]), 
    diff_rec(T, L). 

:-multifile clpfd:dispatch_global/4. 
clpfd:dispatch_global(diff_zero(X, Tail, List), S, S, Actions):- 
    (ground(X) -> 
        filter_diff(List, X, Tail, Actions) 
    ; 
        Actions = [] 
    ). 

filter_diff(_, 0, _, [exit]). 
filter_diff([], _X, _Tail, [exit]). 
filter_diff([Y|T], X, Tail, Actions):- 
    (T==Tail -> 
        Actions = RestActions 
    ; 
        fd_set(Y, SetY), 
        fdset_del_element(SetY, X, NewSetY), 
        Actions = [Y in_set NewSetY | RestActions] 
    ),!,
    filter_diff(T, X, Tail, RestActions). 

all_diff_zero(L, X) :-
  length(L, N),
  length(X, N),
  domain(X, 0, 1),
  mask(L, X, M),
  diff(M),
  !,
  labeling([], X).

For a given list, all_diff_zero predicate should find a binary mask which together with the original list gives a list with all different non-zero items.

After commenting out the line with filter_diff(_, 0, _, [exit])., the diff works as a usual all different constraint (so for example all_diff_zero([ 3 , 1 , 1 ], X). gives me just X = [1,0,1]; X = [1,1,0]).

However, the all different or zero constraint with this line does not work at all:

?- all_diff_zero([ 3 , 1 , 1 ], X).
X = [0,0,0] ? ;
X = [0,0,1] ? ;
X = [0,1,0] ? ;
X = [0,0,0] ? ;
X = [0,0,1] ? ;
X = [0,1,0] ? ;
X = [0,1,0] ? ;
X = [1,0,0] ? ;
X = [1,0,0] ? ;
X = [1,0,1] ? ;
X = [1,0,0] ? ;
X = [1,0,0] ? ;
X = [1,0,1] ? ;
X = [1,0,0] ? ;
X = [1,0,0] ? ;
X = [1,0,1] ? ;
X = [1,0,1] ? ;
X = [1,0,1] ? ;
X = [1,1,0] ? ;
X = [1,1,0] ? ;
X = [1,1,1] ? ; % wrong 
X = [1,1,0] ? ;
X = [1,1,1] ? ; % wrong 
X = [1,1,0] ? ;
X = [1,1,1] ? ; % wrong 
X = [1,1,0] ? ;
X = [1,1,1] ? ; % wrong 

Note that the desired output is:

?- all_diff_zero([ 3 , 1 , 1 ], X).
X = [0,0,0] ? ;
X = [0,0,1] ? ;
X = [0,1,0] ? ;
X = [1,0,0] ? ;
X = [1,1,0] ? ;
X = [1,0,1] ? ;
Tomas Nekvinda
  • 524
  • 1
  • 5
  • 15
  • Removing the mentioned line and adding `... X=:=0 -> Actions = RestActions ; ...` into the `filter_diff` definition seems to work, but still curious why and how that whole thing works :( – Tomas Nekvinda Dec 27 '18 at 12:34

0 Answers0