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] ? ;