1 % (c) 2016-2019 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(clpfd_tables,[can_translate_avl_to_table/2, translate_avl_to_table/2, check_element_of_table/4,
6 check_element_of_avl_with_table/4,
7 clpfd_in_table/2,
8
9 can_translate_function_to_element_constraint/2,
10 check_apply_with_element_constraint/5]).
11
12 :- use_module(module_information,[module_info/2]).
13 :- module_info(group,kernel).
14 :- module_info(description,'Provide interface to CLP(FD) table constraint.').
15
16 % Example usage of CLPFD table/2 :
17 %| ?- table([[X,Y]],[[1,2],[2,3],[3,4],[4,5]]), X in 1..2.
18 %X in 1..2,
19 %Y in 2..3 ?
20
21 :- use_module(library(clpfd)).
22 :- use_module(library(avl),[avl_domain/2]).
23 :- use_module(library(lists),[maplist/3, maplist/4, maplist/2]).
24 :- use_module(kernel_waitflags,[add_fd_variables_for_labeling/2]).
25 :- use_module(debug,[debug_println/2]).
26 :- use_module(error_manager,[add_internal_error/2]).
27
28 % can an AVL set be translated into a CLPFD table membership constraint
29 can_translate_avl_to_table(AVL,SkeletonType) :-
30 AVL = node(Element,_True,_,_,_),
31 translate_to_fd_values(Element,SkeletonType,ElVList,[]),
32 maplist(no_overflow,ElVList), % check that the element constraint will not generate an overflow, e.g., for test 1353
33 % TO DO: ideally we should check avl_min / avl_max (but with multiple FD values in an element; even that will not guarantee that there are no overflows)
34 %(ElVList = [_,_|_] -> true ; print(Element),nl,fail,trace,true),
35 SkeletonType \= boolean. % should we also disallow rec([field(_,boolean)]) % boolean treatment causes problems for 1624, 1627, 140, 1426
36
37 :- use_module(tools,[is_tagged_integer/1]).
38 no_overflow(H) :- (var(H) -> true ; is_tagged_integer(H)).
39
40 translate_avl_to_table(AVL,TableList) :-
41 avl_domain(AVL,List),
42 maplist(translate_avl_element_to_fd_list,List,TableList).
43
44 check_element_of_avl_with_table(X,SkeletonType,AVL,WF) :-
45 translate_avl_to_table(AVL,TableList),
46 check_element_of_table(X,SkeletonType,TableList,WF).
47
48 check_element_of_table(X,SkeletonType,TableList,WF) :-
49 translate_to_fd_values(X,SkeletonType,XList,[]),
50 clpfd_in_table(XList,TableList),
51 add_fd_variables_for_labeling(XList,WF).
52
53 :- use_module(clpfd_interface,[clpfd_inlist/2]).
54 get_unique_el([X],X).
55 clpfd_in_table([X],TableList) :- !, maplist(get_unique_el,TableList,FDList),
56 %print(posting_clpfd_inlist(X,FDList)),nl,
57 clpfd_inlist(X,FDList).
58 clpfd_in_table(XList,TableList) :-
59 %print(posting_clpfd_table([XList],TableList)),nl,
60 clpfd:table([XList],TableList). % we could provide optional third argument, e.g., [order(id3)]
61 % from the SICStus Manual:
62 % table(+Tuples,+Extension,+Options)
63 % Defines an n-ary constraint by extension. Extension should be a list of lists of
64 % integers, each of length n. Tuples should be a list of lists of domain variables,
65 % each also of length n. The constraint holds if every Tuple in Tuples occurs in
66 % the Extension. The constraint will maintain domain consistency.
67 % For convenience, Extension may contain ConstantRange (see Section 10.10.13.1
68 % [Syntax of Indexicals], page 485) expressions in addition to integers.
69
70 % TO DO: also allow partial FD infos, in which case we would use table/2 just for quick propagation
71 % we could also translate variables into ConstantRange expressions (see above)
72 translate_avl_element_to_fd_list(Val,FDList) :- translate_to_fd_values(Val,_,FDList,[]).
73
74 translate_to_fd_values(FD,fd(T)) --> {var(FD),nonvar(T)},!, % setup FD bounds
75 {get_global_type_value(FD,T,X)},[X].
76 translate_to_fd_values(fd(X,T),fd(T)) --> !, [X].
77 translate_to_fd_values(int(A),integer) --> !, [A]. % warning: could overflow, we check above !
78 translate_to_fd_values((A,B),(SkelA,SkelB)) --> !, translate_to_fd_values(A,SkelA), translate_to_fd_values(B,SkelB).
79 translate_to_fd_values(rec(F),rec(SkelF)) --> !, l_translate_to_fd_values(F,SkelF).
80 translate_to_fd_values(Var,Type) --> {var(Var),Type==boolean},!, {prop_01(Var,FD)},[FD].
81 translate_to_fd_values(pred_true,boolean) --> [1].
82 translate_to_fd_values(pred_false,boolean) --> [0].
83 % TO DO: also free-types
84
85 l_translate_to_fd_values([],[]) --> [].
86 l_translate_to_fd_values([field(N,F)|T],[field(N,SF)|ST]) -->
87 translate_to_fd_values(F,SF),l_translate_to_fd_values(T,ST).
88
89 :- block prop_01(-,-).
90 prop_01(pred_true,1).
91 prop_01(pred_false,0).
92
93 % ----------------------
94
95 :- use_module(bsyntaxtree,[is_set_type/2]).
96 :- use_module(preferences,[preference/2]).
97
98 % can a list be translated into CLPFD element constraints for function application
99 can_translate_function_to_element_constraint(List,FunctionType) :-
100 FunctionType \= unknown,
101 is_list_skeleton(List), % TO DO: allow tail AVL list + enable delaying treatment
102 preference(find_abort_values,false),
103 preference(use_chr_solver,false), % avoid falling into the SICStus element/3 bug prior to 4.3.6; TO DO: remove later
104 is_set_type(FunctionType,couple(Domain,Range)),
105 is_simple_fd_type(Domain), % Domain must be translatable to simple FD value list
106 is_fd_type(Range).
107
108 is_list_skeleton(X) :- nonvar(X),is_list_skeleton_aux(X).
109 is_list_skeleton_aux([]).
110 is_list_skeleton_aux([_H|T]) :- is_list_skeleton(T).
111
112 is_simple_fd_type(global(_)).
113 is_simple_fd_type(integer).
114 is_simple_fd_type(boolean).
115
116 is_fd_type(couple(A,B)) :- !, is_fd_type(A), is_fd_type(B).
117 is_fd_type(record(F)) :- !, is_fd_field_types(F).
118 is_fd_type(X) :- is_simple_fd_type(X).
119
120 is_fd_field_types([]).
121 is_fd_field_types([field(_,FT)|T]) :- is_fd_type(FT), is_fd_field_types(T).
122
123 :- use_module(b_global_sets,[get_global_type_value/3]).
124 get_fd_type(couple(A,B),(VA,VB)) --> get_fd_type(A,VA), get_fd_type(B,VB).
125 get_fd_type(global(T),FD) --> {get_global_type_value(FD,T,X)}, [X].
126 get_fd_type(integer,int(X)) --> [X].
127 get_fd_type(boolean,B) --> [FD], {prop_01(B,FD)}.
128 get_fd_type(record(Fields),rec(VF)) --> get_field_fd_types(Fields,VF).
129
130 get_field_fd_types([],[]) --> [].
131 get_field_fd_types([field(Name,Type)|FT],[field(Name,VF)|VT]) -->
132 get_fd_type(Type,VF),
133 get_field_fd_types(FT,VT).
134
135 get_fd_types(Domain,Range,(VD,VR),FDDom,FDRList) :- get_fd_type(Domain,VD,[FDDom],[]), get_fd_type(Range,VR,FDRList,[]).
136
137 % Examples:
138 % f = {x|->2, y|->3} & x:1..2 & y:4..5 & r = f(4)
139 % f = {x|->(2,x+1), y|->(3,y+1)} & x:1..2 & y:4..5 & r = f(v) & v:3..4 --> deterministically sets v=4, r=(3,5),y=4
140 % from the SICStus manual:
141 % element(?X,+List,?Y)
142 % where X and Y are domain variables and List is a list of domain variables. True
143 % if the X:th element of List is Y. Operationally, the domains of X and Y are
144 % constrained so that for every element in the domain of X, there is a compatible
145 % element in the domain of Y, and vice versa.
146 % Maintains domain consistency in X and bounds consistency in List and Y.
147
148 :- use_module(library(clpfd),[all_distinct/1]).
149 :- use_module(library(lists),[nth1/3]).
150 check_apply_with_element_constraint(List,X,Y,FunctionType,WF) :-
151 is_set_type(FunctionType,couple(Domain,Range)),
152 get_fd_type(Domain,X,[FDX],[]),
153 if(l_get_fd_types(List,DomainList,RangeLists,Domain,Range,FDX,FoundY),true,
154 (add_internal_error('Get FD Types failed: ',get_fd_types(Domain,Range)),fail)),
155 debug_println(4,element_domain_list(FDX,DomainList)),
156 %print(element_domain_list(FDX,DomainList,FoundY)),nl,
157 (FoundY \== y_not_found
158 -> FoundY=Y % no need for equal_object: we only have FD types + Bool + couples
159 ; element(Idx,DomainList,FDX), % no need to call equal_object for X as we have simple fd types
160 (nonvar(Idx)
161 -> nth1(Idx,List,(X,Y)) %relevant for tests 387, 1579
162 ; all_distinct(DomainList), % ensures that Idx will get bound eventually if we enumerate DomainList and X (if multiple entries in DomainList then Idx would not get bound)
163 maplist(get_fd_types(Domain,Range),List,DomainList,RangeLists),
164 get_fd_type(Range,Y,FDYList,[]),
165 setup_element_domain_constraints(FDYList,Idx,RangeLists),
166 add_fd_variables_for_labeling([FDX|FDYList],WF)
167 )
168 ).
169
170 % an unfolded version of maplist(get_fd_types(Domain,Range),List,DomainList,RangeLists)
171 % we also check if the lookup FD variable occurs in the list; if so we return the Y value in Found
172 % relevant for performance, e.g., for NQueens60; we avoid traversing the list a second time
173 l_get_fd_types([],[],[],_,_,_,y_not_found).
174 l_get_fd_types([Pair|PT],[FDDom|DT],[FDRList|RT],Domain,Range,FDX,FoundY) :-
175 get_fd_types(Domain,Range,Pair,FDDom,FDRList),
176 (FDX==FDDom -> Pair=(_,FoundY), DT=[],RT=[]
177 ; l_get_fd_types(PT,DT,RT,Domain,Range,FDX,FoundY)).
178
179
180 setup_element_domain_constraints([],_,_).
181 setup_element_domain_constraints([FDY|YT],Idx,RangeLists) :-
182 maplist(get_next_fd,RangeLists,FDYList,RestLists),
183 %print(setting_up_range_element_constraint(Idx,FDYList,FDY)),nl,
184 element(Idx,FDYList,FDY),
185 setup_element_domain_constraints(YT,Idx,RestLists).
186
187 get_next_fd([H|T],H,T).