1 | % (c) 2009-2024 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(b_enumerate, [ | |
6 | b_type_values_in_store/3, | |
7 | b_enumerate_values_in_store/5, | |
8 | %b_enumerate_values/2, | |
9 | b_enumerate_typed_values/3, | |
10 | %b_tighter_enumerate_values_in_store/4, | |
11 | b_tighter_enumerate_all_values/2, | |
12 | b_tighter_enumerate_values/3, | |
13 | b_tighter_enumerate_values_in_ctxt/3, | |
14 | b_tighter_enumerate_single_value/5, | |
15 | ||
16 | b_extract_typedvalc/5, | |
17 | construct_typedval_infos/4, | |
18 | ||
19 | extract_do_not_enum_ids_from_predicate/2 | |
20 | %enumerate_integer_with_min_range_wf/4 % no longer used | |
21 | ]). | |
22 | ||
23 | ||
24 | :- use_module(self_check). | |
25 | :- use_module(debug). | |
26 | :- use_module(kernel_objects). | |
27 | :- use_module(kernel_waitflags). | |
28 | :- use_module(error_manager,[add_internal_error/2]). | |
29 | ||
30 | :- use_module(module_information,[module_info/2]). | |
31 | :- module_info(group,interpreter). | |
32 | :- module_info(description,'This module takes care of enumerating B variables.'). | |
33 | ||
34 | /* ------------------------------------------ */ | |
35 | ||
36 | ||
37 | /* can be used to apply the result of | |
38 | b_getVarTypes_from_boolean_expression to a store */ | |
39 | ||
40 | ||
41 | :- use_module(typechecker,[type_check/2]). | |
42 | :- assert_pre(b_enumerate:b_type_values_in_store(V,T,S), | |
43 | (typechecker:type_check(V,list(variable_id)), | |
44 | typechecker:type_check(T,list(basic_type_descriptor)),typechecker:type_check(S,store))). | |
45 | :- assert_post(b_enumerate:b_type_values_in_store(_,_,_), true). | |
46 | ||
47 | b_type_values_in_store([],[],_). | |
48 | b_type_values_in_store([Variable|VT],[Type|TT],Store) :- | |
49 | b_type_value_in_store(Variable,Type,Store), | |
50 | b_type_values_in_store(VT,TT,Store). | |
51 | ||
52 | :- use_module(store,[lookup_value_for_existing_id/3]). | |
53 | b_type_value_in_store(Var,Type,Store) :- | |
54 | (Var = 'Identifier'(VarID) | |
55 | -> add_internal_error('Variable not in proper form: ',b_type_value_in_store(Var,Type,_)) | |
56 | ; VarID = Var), | |
57 | lookup_value_for_existing_id(VarID,Store,Val), | |
58 | (Val==fail | |
59 | -> add_internal_error('Variable has not been given value: ',b_type_value_in_store(Var,Type,Store)) | |
60 | ; kernel_objects:basic_type(Val,Type)). | |
61 | ||
62 | ||
63 | ||
64 | ||
65 | /* ------------------------------------------ */ | |
66 | ||
67 | ||
68 | :- assert_pre(b_enumerate:b_enumerate_values_in_store(V,T,_,S,_WF), | |
69 | (typechecker:type_check(V,list(variable_id)), | |
70 | typechecker:type_check(T,list(basic_type_descriptor)),typechecker:type_check(S,store))). | |
71 | :- assert_post(b_enumerate:b_enumerate_values_in_store(_,_,_,_,_), true). | |
72 | ||
73 | % note: assumes no lambda_result variables in scope | |
74 | b_enumerate_values_in_store(Variables,Types,Values,Store,WF) :- | |
75 | generate_typed_var_list(Variables,Types,Store,Values,TypedValues), | |
76 | b_tighter_enumerate_all_values(TypedValues,WF). | |
77 | ||
78 | :- block generate_typed_var_list(-,?,?,?,?). | |
79 | generate_typed_var_list([],[],_,[],[]). | |
80 | generate_typed_var_list([VarID|VT],[Type|TT],Store,Values,Res) :- | |
81 | (store:lookup_value_for_existing_id(VarID,Store,Val), Val \== fail | |
82 | -> construct_typedval_infos(VarID,[],trigger_true(VarID),TINFO), | |
83 | Res = [typedval(Val,Type,VarID,TINFO)|Rest], % always trigger enumeration warning | |
84 | Values=[Val|ValT] | |
85 | ; add_internal_error('Variable has not been given value: ',VarID), | |
86 | Res=Rest,Values=ValT | |
87 | ), | |
88 | generate_typed_var_list(VT,TT,Store,ValT,Rest). | |
89 | ||
90 | ||
91 | /* ------------------------------------------ */ | |
92 | ||
93 | b_enumerate_typed_values(Values,Types,WF) :- % called by b_execute_eventb_action(becomes_element_of, ... | |
94 | b_generate_typed_values(Values,Types,TypedVals), | |
95 | b_tighter_enumerate_all_values(TypedVals,WF). | |
96 | ||
97 | % generated typed values trigger the enumeration warning | |
98 | b_generate_typed_values([],[],[]). | |
99 | b_generate_typed_values([Val|Vrest],[Type|Trest],[typedval(Val,Type,'$UNKNOWN_ID',TINFO)|TVrest]) :- | |
100 | construct_typedval_infos('$unknown',[],trigger_true(unknown),TINFO), | |
101 | b_generate_typed_values(Vrest,Trest,TVrest). | |
102 | ||
103 | :- use_module(library(ordsets),[ord_member/2]). | |
104 | is_lambda_result_or_useless(ID,TINFO,DoNotEnumIds) :- | |
105 | (is_lambda_result_id(ID) -> true | |
106 | ; is_lambda_result_annotated(ID,TINFO) -> true | |
107 | ; ord_member(ID,DoNotEnumIds)). | |
108 | ||
109 | is_lambda_result_id('_lambda_result_'). % TO DO: use lambda_result info field ? | |
110 | %is_lambda_result_id(caval__msg__all). is_lambda_result_id(data_verified__index__0). is_lambda_result_id(data_verified__index__1). is_lambda_result_id(status). | |
111 | is_lambda_result_annotated(ID,lambda_result(Trigger)) :- | |
112 | (var(Trigger) -> add_internal_error('Illegal trigger for enumeration variable:',ID),fail | |
113 | ; true). | |
114 | ||
115 | % store relevant infos from AST info list for use in typedval(_,_,_,INFO) field | |
116 | construct_typedval_infos(VarID,Infos,Trigger,Res) :- | |
117 | ? | member(Info,Infos), |
118 | lambda_result_info(Info,VarID), | |
119 | !, | |
120 | Res = lambda_result(Trigger). | |
121 | construct_typedval_infos(_,_,Trigger,Trigger). | |
122 | ||
123 | lambda_result_info(lambda_result(VarID),VarID). | |
124 | lambda_result_info(prob_annotation('DO_NOT_ENUMERATE'(VarID)),VarID). | |
125 | % TO DO: merge into one sorted info field: lambda_result(SortedIds) | |
126 | ||
127 | % extract enumeration warning from Trigger Info field | |
128 | get_enum_warning_info(lambda_result(Trigger),Res) :- !, Res=Trigger. | |
129 | get_enum_warning_info(Trigger,Trigger). | |
130 | ||
131 | % add enum_Warning wrapper to trigger if not already there | |
132 | add_enum_warning_info(lambda_result(Trigger),Res) :- !, Res=lambda_result(Trigger). | |
133 | add_enum_warning_info(Trigger,lambda_result(Trigger)). | |
134 | ||
135 | ||
136 | % only called by b_not_test_closure_enum: | |
137 | b_extract_typedvalc([],[],_,_,[]). | |
138 | b_extract_typedvalc([Variable|VT],[Type|TT],Infos,Store,Res) :- | |
139 | (is_lambda_result_or_useless(Variable,Infos,[]) % TO DO: check if we can pass DO_NOT_ENUMERATE Infos | |
140 | % TO DO: use lambda_result info field ?; Note the check is repeated below in sort_typed_values | |
141 | -> b_extract_typedvalc(VT,TT,Infos,Store,Res) | |
142 | % lambda result does not need to be enumerated; will be computed from rest | |
143 | % TO DO: should we also deal specially with _lambda_result_ below in b_tighter_enumerate_values; | |
144 | % or even infer in ANY statements, ... which variables are simply defined by equalities from the others | |
145 | ; Res=[ExtractedH|ET], | |
146 | b_extract_typedvalc_var(Variable,Type,Infos,Store,ExtractedH), | |
147 | b_extract_typedvalc(VT,TT,Infos,Store,ET)). | |
148 | ||
149 | b_extract_typedvalc_var(Var,Type,Infos,Store,typedval(Val,Type,VarID,TINFO)) :- | |
150 | (Var = 'Identifier'(VarID) | |
151 | -> print(b_tighter_enumerate_value_in_store(Var,Type)),print(' variable not in proper form'),nl | |
152 | ; VarID = Var), | |
153 | construct_typedval_infos(VarID,Infos,trigger_true(VarID),TINFO), | |
154 | (store:lookup_value_for_existing_id(VarID,Store,Val), Val \== fail | |
155 | -> true | |
156 | ; add_internal_error('Variable has not been given value: ',VarID) | |
157 | ). | |
158 | ||
159 | % update priority for certain types | |
160 | % TO DO: we should probably group all infinite integer values together and take the variable with the smallest size | |
161 | ||
162 | :- use_module(kernel_tools,[ground_value/1]). | |
163 | b_tighter_enumerate_sorted_values([],_WF). | |
164 | b_tighter_enumerate_sorted_values([typedvalc(Val,Type,VarID,EnumWarningInfos,Card)|Rest],WF) :- | |
165 | (ground_value(Val) | |
166 | -> check_reached(Val,Type,VarID,Card,det,WF), %% <----- | |
167 | b_tighter_enumerate_sorted_values(Rest,WF) %% no need to enumerate this value | |
168 | ; treat_next_typed_valc(Type,Val,VarID,EnumWarningInfos,Card,Rest,WF)). | |
169 | ||
170 | :- use_module(library(lists)). | |
171 | :- use_module(clpfd_interface,[ clpfd_size/2]). | |
172 | :- use_module(kernel_waitflags,[large_finite_priority/1]). | |
173 | treat_next_typed_valc(integer,Val,VarID,EnumWarningInfos,_Card,Rest,WF) :- | |
174 | nonvar(Val), Val=int(FD), | |
175 | clpfd_size(FD,Size), % will return sup if no bounds + also works in non-CLPFD mode | |
176 | number(Size),!, | |
177 | treat_next_typed_valc_normal(integer,Val,VarID,EnumWarningInfos,Size,Rest,WF). | |
178 | treat_next_typed_valc(integer,Val,VarID,EnumWarningInfos,Card,Rest,WF) :- | |
179 | !, | |
180 | large_finite_priority(LP), | |
181 | treat_next_integer(first_iteration,[LP],Val,VarID,EnumWarningInfos,Card,Rest,WF). % TO DO: maybe do several iterations 1000,LP | |
182 | % however: test 1003 with plavis-TransData_SP_13.prob fails in this case; to do: investigate | |
183 | treat_next_typed_valc(Type,Val,VarID,EnumWarningInfos,Card,Rest,WF) :- % TO DO: also look for finite values in Rest if Type is infinite but not integer | |
184 | treat_next_typed_valc_normal(Type,Val,VarID,EnumWarningInfos,Card,Rest,WF). | |
185 | ||
186 | % compute current priority of typedvalc; to be compared with @< | |
187 | get_typedvalc_priority(typedvalc(Val,Type,_,Enum,_Prio),Res) :- %Type=integer, | |
188 | (Enum = lambda_result(_) -> Res=sup(1) % do not give priority to lambda_result | |
189 | ; get_typedvalc_finite_size(Type,Val,Size) | |
190 | -> Res=Size % we have a finite value: use size | |
191 | ; degree_value(Type,Val,Dg) | |
192 | -> NDg is -Dg, Res=sup(NDg) % use negative number of degree, so that larger degrees get priority | |
193 | % relevant for e.g. test 1484: res=x*x+4*x+14 | |
194 | ; Res = sup(1) | |
195 | ). | |
196 | get_typedvalc_finite_size(Type,Val,Size) :- | |
197 | finite_value(Type,Val,Size), | |
198 | (Size=1 -> true ; | |
199 | \+ currently_useless_variable(Val)). % ,print(finite(Val,Size)),nl. | |
200 | ||
201 | :- use_module(library(clpfd),[fd_degree/2]). | |
202 | % try and get degree of a value | |
203 | degree_value(integer,Val,Dg) :- %translate:print_value_variable(Val),nl, | |
204 | nonvar(Val), Val=int(FD), fd_degree(FD,Dg), number(Dg). | |
205 | ||
206 | :- use_module(kernel_card_arithmetic,[safe_mul/3]). | |
207 | :- use_module(b_global_sets,[b_global_set_cardinality/2]). | |
208 | finite_value(integer,Val,Size) :- nonvar(Val), Val=int(FD), clpfd_size(FD,Size), number(Size). | |
209 | finite_value(boolean,Val,Size) :- (ground(Val) -> Size = 1 ; Size =2). | |
210 | finite_value(string(_),Val,Size) :- ground(Val), Size=1. | |
211 | finite_value(real(_),Val,Size) :- ground(Val), Size=1. | |
212 | finite_value(global(G),Val,Size) :- (ground(Val) -> Size = 1 ; b_global_set_cardinality(G,Size)). | |
213 | finite_value(set(T),Val,Size) :- nonvar(Val), l_finite_value(T,Val,Size). | |
214 | finite_value(couple(T1,T2),Val,Size) :- nonvar(Val), Val = (V1,V2), | |
215 | finite_value(T1,V1,Size1), | |
216 | finite_value(T2,V2,Size2), | |
217 | safe_mul(Size1,Size2,Size), number(Size). % can also be inf_overflow ! | |
218 | finite_value(record(Fields),Val,Size) :- | |
219 | nonvar(Val), Val=rec(FVals), | |
220 | finite_fields(Fields,FVals,Size), nl,print(finite_field(Val,Size)),nl,nl. | |
221 | % TO DO: missing freetypes | |
222 | ||
223 | l_finite_value(_Type,Val,_) :- var(Val),!,fail. | |
224 | l_finite_value(_,avl_set(_),1). | |
225 | l_finite_value(_Type,[],1). | |
226 | l_finite_value(Type,[H|T],Size) :- l_finite_value(Type,T,SizeT), finite_value(Type,H,SizeH), | |
227 | safe_mul(SizeT,SizeH,Size), number(Size). | |
228 | ||
229 | finite_fields(_Type,Val,_) :- var(Val),!,fail. | |
230 | finite_fields([],[],1). | |
231 | finite_fields([HType|TTypes],[H|T],Size) :- finite_fields(TTypes,T,SizeT), finite_value(HType,H,SizeH), | |
232 | safe_mul(SizeT,SizeH,Size), number(Size). | |
233 | ||
234 | % find the typedvalc (Selected) with the smallest value (Prio) and return other (Rest) typedvalc-values | |
235 | smallest_value([H|T],Selected,Rest,Prio) :- | |
236 | get_typedvalc_priority(H,HPrio), | |
237 | smallest_value_aux(T,H,HPrio,Selected,Rest,Prio). | |
238 | ||
239 | smallest_value_aux([],H,HPrio,H,[],HPrio). | |
240 | smallest_value_aux([H|T],Acc,AccPrio,Selected,Rest,SelPrio) :- | |
241 | get_typedvalc_priority(H,HPrio), | |
242 | (HPrio=1 -> smallest_value_aux(T,Acc,AccPrio,Selected,Rest,SelPrio) % remove this TC from list; does not need to be enumerated anymore | |
243 | ; HPrio @< AccPrio | |
244 | -> Rest = [Acc|TRest], | |
245 | smallest_value_aux(T,H,HPrio,Selected,TRest,SelPrio) | |
246 | ; Rest = [H|TRest], | |
247 | smallest_value_aux(T,Acc,AccPrio,Selected,TRest,SelPrio) | |
248 | ). | |
249 | ||
250 | % we try again and see if the domain is now finite | |
251 | % ideally we should register all integer variables in the kernel_waitflag FD list; maybe we should have a finite and infinite list? or use our own labeling get-next function clpfd_get_next_variable_to_label_ffc adapted for infinite domains | |
252 | :- block treat_next_integer(-,?,?,?,?,?,?,?). | |
253 | treat_next_integer(LWF,_,Val,VarID,EnumWarning,_Card,Rest,WF) :- LWF \== first_iteration, % print(treat(Val,VarID,Rest)),nl, | |
254 | nonvar(Val), Val=int(FD), | |
255 | clpfd_size(FD,Size), % will return sup if no bounds + also works in non-CLPFD mode | |
256 | %print(treat_next_integer(VarID,Val,Size,_Card,Rest)),nl, | |
257 | number(Size),!, | |
258 | (Size = 1 % for Size=1 no enumeration is necessary anyway | |
259 | -> b_tighter_enumerate_sorted_values(Rest,WF) % no enumeration required | |
260 | ; treat_next_typed_valc_normal(integer,Val,VarID,EnumWarning,Size,Rest,WF)). | |
261 | treat_next_integer(_,Prios,Val,VarID,EnumWarning,Card,OtherTCs,WF) :- % ideally we should move this above the clause with NextPrio; but then test 412 fails | |
262 | CurTC = typedvalc(Val,integer,VarID,EnumWarning,Card), | |
263 | smallest_value([CurTC|OtherTCs],TC,Rest,Size), | |
264 | (number(Size) % finite domain found | |
265 | ; | |
266 | Prios = [] % no more delaying; we have to enumerate an unbounded value | |
267 | ), | |
268 | !, | |
269 | (Size == 1 % no enumeration required for this variable | |
270 | -> b_tighter_enumerate_sorted_values(Rest,WF) | |
271 | ; treat_next_typed_valc_normal4(TC,Size,Rest,WF) | |
272 | ). | |
273 | treat_next_integer(_,[NextPrio|Prios],Val,VarID,EnumWarning,Card,Rest,WF) :- !, % for test 1161 this clause causes a considerable slowdown | |
274 | get_wait_flag(NextPrio,treat_next_integer(VarID,Val),WF,LWF), | |
275 | treat_next_integer(LWF,Prios,Val,VarID,EnumWarning,Card,Rest,WF). | |
276 | treat_next_integer(_,_,Val,VarID,EnumWarning,Card,Rest,WF) :- | |
277 | treat_next_typed_valc_normal(integer,Val,VarID,EnumWarning,Card,Rest,WF). % we have already delayed; no need to delay further | |
278 | ||
279 | treat_next_typed_valc_normal4(typedvalc(Val,Type,VarID,EnumWarning,Card),Size,Rest,WF) :- | |
280 | (number(Size) -> NewCard=Size ; NewCard=Card), % use narrowed down card if possible | |
281 | treat_next_typed_valc_normal(Type,Val,VarID,EnumWarning,NewCard,Rest,WF). | |
282 | treat_next_typed_valc_normal(Type,Val,VarID,EnumWarning,Card,Rest,WF) :- | |
283 | get_wait_flag(Card,tighter_enum(VarID,Val,Type),WF,LWF), | |
284 | b_tighter_enumerate_sorted_value_and_continue(LWF,Val,Type,VarID,EnumWarning,Card,Rest,WF). | |
285 | ||
286 | :- block b_tighter_enumerate_sorted_value_and_continue(-,?,?,?,?,?,?,?). | |
287 | b_tighter_enumerate_sorted_value_and_continue(_LWF,Val,Type,VarID,EnumWarning,Card,Rest,WF) :- | |
288 | (Rest = [] -> true ; get_minimum_waitflag_prio(WF,Prio,_), Prio < 20000), | |
289 | % only check for useless if there are either other variables to chose from or other WF goals | |
290 | currently_useless_variable(Val), | |
291 | !, | |
292 | ? | b_tighter_enumerate_useless(Val,Type,VarID,EnumWarning,Card,Rest,WF). |
293 | b_tighter_enumerate_sorted_value_and_continue(_LWF,Val,Type,VarID,EnumWarning,Card,Rest,WF) :- | |
294 | check_reached(Val,Type,VarID,Card,WF), %% <----- | |
295 | %% tools:print_bt_message(enumerate(VarID,Val)), %% | |
296 | ? | enum_tight_with_wf(Val,Type,EnumWarning,WF), |
297 | %% tools:print_bt_message(after_enumerate(VarID,Val)), | |
298 | %(VarID=='VS_NOERRORCOND' -> print(done_enum(VarID,Val)),nl ; true), | |
299 | b_tighter_enumerate_sorted_values(Rest,WF). | |
300 | ||
301 | b_tighter_enumerate_useless(Val,Type,VarID,EnumWarning,Card,Rest,WF) :- | |
302 | get_next_useful_variable(Rest,RVal,RType,RVarID,REnumWarning,RCard,RRest), % ,print(using(RVarID,RVal,RType)),nl | |
303 | !, | |
304 | %debug_println(9,skipping(Val,VarID,Card,new(RVal,RType,RVarID,RCard))), | |
305 | (RCard =< Card /* we can enumerate straight away */ | |
306 | -> check_reached(RVal,RType,RVarID,RCard,WF), %% <------ | |
307 | enum_tight_with_wf(RVal,RType,EnumWarning,WF), | |
308 | b_tighter_enumerate_sorted_values([typedvalc(Val,Type,VarID,EnumWarning,Card)|RRest],WF) | |
309 | ; b_tighter_enumerate_sorted_values([typedvalc(RVal,RType,RVarID,REnumWarning,RCard), | |
310 | typedvalc(Val,Type,VarID,EnumWarning,Card)|RRest],WF) | |
311 | ). | |
312 | b_tighter_enumerate_useless(Val,Type,VarID,EnumWarning,Card,Rest,WF) :- | |
313 | %debug_println(19,all_variables_useless(Val,Card)), | |
314 | get_minimum_waitflag_prio(WF,Prio,_Info), | |
315 | Prio < 20000, % avoid infinite enumeration ... TO DO: improve check | |
316 | %debug_println(19,all_variables_useless_info(Prio,Info)), | |
317 | !, | |
318 | ? | ground_wait_flag_to(WF,Prio), |
319 | % Now try again: | |
320 | b_tighter_enumerate_sorted_value_and_continue(1,Val,Type,VarID,EnumWarning,Card,Rest,WF). | |
321 | b_tighter_enumerate_useless(Val,Type,VarID,EnumWarning,Card,Rest,WF) :- | |
322 | check_reached(Val,Type,VarID,Card,WF), %% <----- | |
323 | %% tools:print_bt_message(enumerate(VarID,Val)), %% | |
324 | ? | enum_tight_with_wf(Val,Type,EnumWarning,WF), |
325 | %% tools:print_bt_message(after_enumerate(VarID,Val)), | |
326 | %(VarID=='VS_NOERRORCOND' -> print(done_enum(VarID,Val)),nl ; true), | |
327 | b_tighter_enumerate_sorted_values(Rest,WF). | |
328 | ||
329 | % -------------------- | |
330 | ||
331 | ||
332 | :- use_module(kernel_objects,[enumerate_tight_type_wf/4]). | |
333 | enum_tight_with_wf(Val,Type,EnumWarningInfos,WF) :- | |
334 | get_enum_warning_info(EnumWarningInfos,EnumWarning), | |
335 | ? | enumerate_tight_type_wf(Val,Type,EnumWarning,WF). |
336 | ||
337 | ||
338 | % --------------------- | |
339 | ||
340 | get_next_useful_variable([typedvalc(Val,Type,VarID,EnumWarning,Card)|Rest],RVal,RType,RVarID,REnumWarning,RCard,RRest) :- | |
341 | (currently_useless_variable(Val) | |
342 | -> RRest=[typedvalc(Val,Type,VarID,EnumWarning,Card)|TRRest], | |
343 | get_next_useful_variable(Rest,RVal,RType,RVarID,REnumWarning,RCard,TRRest) | |
344 | ; RRest=Rest, RVal=Val, RType=Type, RVarID=VarID, REnumWarning = EnumWarning, RCard=Card | |
345 | ). | |
346 | ||
347 | ||
348 | /* purpose: check if an enumeration is useful or not and give priority to useful variables ? */ | |
349 | % relevant for tests 1368 and 1492 | |
350 | %:- use_module(library(clpfd),[fd_dom/2]). | |
351 | currently_useless_variable(Val) :- var(Val),!,frozen(Val,Goal), % this can be expensive when Goal is large ! | |
352 | (enumeration_only_goal(Goal,Val) -> true). | |
353 | currently_useless_variable(int(Val)) :- currently_useless_variable(Val). | |
354 | currently_useless_variable(fd(Val,_)) :- currently_useless_variable(Val). | |
355 | currently_useless_variable(string(Val)) :- currently_useless_variable(Val). | |
356 | ||
357 | :- use_module(clpfd_interface,[clpfd_degree/2]). | |
358 | enumeration_only_goal(true,_). | |
359 | enumeration_only_goal((A,B),Val) :- | |
360 | (enumeration_only_goal(A,Val) -> enumeration_only_goal(B,Val) | |
361 | ; % we have not detected A as enumeration only: check if we can find an annotation not to enumerate | |
362 | force_enumeration_only_goal(A,Val) % Note: we only check first component; on the assumption that these co-routines are added early and to avoid useless lookups; TO DO: is there a better solution ? | |
363 | ). | |
364 | enumeration_only_goal(dif(A,B),Var) :- (Var==A ; Var==B). % SWI | |
365 | enumeration_only_goal(Module:Call,Val) :- enumeration_only_goal_m(Module,Call,Val). | |
366 | ||
367 | %enumeration_only_goal_m(fd_utils_clpfd:delayed_enum_fd(V,_,_,_),Val) :- V==Val. | |
368 | enumeration_only_goal_m(kernel_objects,call_enumerate_int(V,_,_,_),Val) :- !, V==Val. | |
369 | enumeration_only_goal_m(kernel_objects,enumerate_int_wf(V,_,_),Val) :- !, V==Val. | |
370 | enumeration_only_goal_m(kernel_objects,enumerate_natural(V,_,_,_),Val) :- !, V==Val. | |
371 | enumeration_only_goal_m(prolog,dif(A,B),Var) :- !, (Var==A ; Var==B). | |
372 | enumeration_only_goal_m(clpfd,(X in _),Var) :- !, Var==X, | |
373 | clpfd_degree(X,0). % only goal is the enumerator itself; | |
374 | % but CLPFD constraints are ***NOT*** shown in frozen(...) so we need to call clpfd_degree | |
375 | enumeration_only_goal_m(bsets_clp,is_sequence_wf_ex(V,GS,_,_),Val) :- V==Val, | |
376 | nonvar(GS),GS=global_set(_). % only constraint: we should generate a sequence | |
377 | enumeration_only_goal_m(external_functions,string_to_int2(_,V,_,_),Val) :- !, V==Val. | |
378 | enumeration_only_goal_m(external_functions,printf(_,V,_,_),Val) :-!, V==Val. | |
379 | enumeration_only_goal_m(external_functions,block_print_value(_,_,_V,_,_),_Val) :- !. % from observe | |
380 | enumeration_only_goal_m(external_functions,block_print_value_complete(_,_,_,_,_),_Val). % from observe | |
381 | enumeration_only_goal_m(b_interpreter_components,observe_variable_block(_,_,_,_,_),_) :- !. % in -p TRACE_INFO TRUE mode | |
382 | enumeration_only_goal_m(b_interpreter_components,observe_variable1_block(_,_,_,_),_). | |
383 | enumeration_only_goal_m(prolog,trig_ground(A1,_,_,_,Trigger),Val) :- Val==A1, | |
384 | triggers_better_enumeration(Trigger,Val,3). % if this only triggers an enumeration of Val itself; it is better to let the other variable trigger the enumeration | |
385 | ||
386 | force_enumeration_only_goal((A,B),Val) :- | |
387 | (force_enumeration_only_goal(A,Val) -> true ; force_enumeration_only_goal(B,Val) ). | |
388 | force_enumeration_only_goal(external_functions:'DO_NOT_ENUMERATE'(V,_,_),Val) :- V==Val. | |
389 | ||
390 | % check if Trigger either triggers a better enumerator or just debugging code | |
391 | triggers_better_enumeration(Trigger,Val,Depth) :- Depth>0, D1 is Depth-1, | |
392 | frozen(Trigger,TrigGoal), | |
393 | ( better_enum(TrigGoal,Trigger,Val,D1) -> true). | |
394 | ||
395 | better_enum((A,B),Trigger,Val,Depth) :- better_enum(A,Trigger,Val,Depth), better_enum(B,Trigger,Val,Depth). | |
396 | better_enum(prolog:trig_and(_,_,_,_,Trigger1),Trigger,Val,Depth) :- !, | |
397 | (Trigger1==Trigger -> true ; triggers_better_enumeration(Trigger1,Val,Depth)). | |
398 | better_enum(prolog:trig_or(_,Trigger1,_),Trigger,Val,Depth) :- !, | |
399 | (Trigger1==Trigger -> true ; triggers_better_enumeration(Trigger1,Val,Depth)). | |
400 | better_enum(prolog:trig_ground(VV,_,_,Trigger1,_),Trigger,Val,Depth) :- !, | |
401 | (Trigger1==Trigger -> true ; VV=Val -> true ; triggers_better_enumeration(Trigger1,Val,Depth)). | |
402 | better_enum(prolog:when(WT,_,G), Trigger,Val,Depth) :- !, | |
403 | Trigger==WT, better_enum(G,Trigger,Val,Depth). | |
404 | better_enum(custom_explicit_sets:couple_element_of_avl_set(_X,_Y,_AVL,_WF1,_WF),_,_Val,_Depth) :- !, | |
405 | % TO DO: add more enumerators of Val | |
406 | % TO DO: VAL occurs in _X !! | |
407 | true. | |
408 | better_enum(external_functions:G,_,_Val,_Depth) :- external_debug_function(G). | |
409 | ||
410 | external_debug_function(fprintf2(_,_,_,_)). % TO DO: add more | |
411 | ||
412 | ||
413 | ||
414 | b_tighter_enumerate_single_value(Val,Type,Infos,VarID,WF) :- | |
415 | construct_typedval_infos(VarID,Infos,trigger_true(VarID),TINFO), | |
416 | b_tighter_enumerate_all_values([typedval(Val,Type,VarID,TINFO)],WF). | |
417 | ||
418 | b_tighter_enumerate_all_values(TypedValues,WF) :- | |
419 | b_tighter_enumerate_values(TypedValues,enum_info([],[]),WF). | |
420 | ||
421 | % a variation of b_tighter_enumerate_values/2 which obtains a context predicate | |
422 | % which may contain DO_NOT_ENUMERATE annotations | |
423 | % and which is analysed if some ids are better enumerated inside the predicate itself | |
424 | % (the latter was previously done by project_away_useless_enumeration_values) | |
425 | b_tighter_enumerate_values_in_ctxt(TypedValues,ContextPred,WF) :- | |
426 | extract_do_not_enum_ids_from_predicate(ContextPred,UselessIds), | |
427 | b_tighter_enumerate_values(TypedValues,UselessIds,WF). | |
428 | ||
429 | % enumerate given a list of useless ids (marked e.g. by DO_NOT_ENUMERATE) | |
430 | b_tighter_enumerate_values(TypedValues,enum_info(UselessIds,DelayedIds),WF) :- !, | |
431 | %print(sort_typed(UselessIds,DelayedIds)),nl, | |
432 | sort_typed_values(TypedValues,[],UselessIds,DelayedIds,SortedValues), | |
433 | b_tighter_enumerate_sorted_values(SortedValues,WF). | |
434 | b_tighter_enumerate_values(TV,EI,WF) :- | |
435 | add_internal_error('Illegal call: ',b_tighter_enumerate_values(TV,EI,WF)), | |
436 | fail. | |
437 | ||
438 | % ------------------------------- | |
439 | ||
440 | % extract identifiers which are either marked as DO_NOT_ENUMERATED or | |
441 | % whose enumeration is pointless given the predicate body | |
442 | extract_do_not_enum_ids_from_predicate(b(Pred,_,Infos),enum_info(SortedUselessIds,DelayedIds)) :- !, | |
443 | findall(ID,member(prob_annotation('DO_NOT_ENUMERATE'(ID)),Infos),UselessIds1), | |
444 | findall(ID,find_useless_enumeration_id(Pred,ID),UselessIds,UselessIds1), | |
445 | findall(delay_enum(PosNr,ID),member(prob_annotation('DELAY_ENUMERATION'(PosNr,ID)),Infos),DelayedIds), | |
446 | sort(UselessIds,SortedUselessIds). | |
447 | extract_do_not_enum_ids_from_predicate(P,Res) :- | |
448 | add_internal_error('Illegal call: ',extract_do_not_enum_ids_from_predicate(P,Res)), | |
449 | Res=enum_info([],[]). | |
450 | ||
451 | :- use_module(bsyntaxtree, [get_texpr_expr/2]). | |
452 | :- use_module(preferences, [get_preference/2]). | |
453 | % TO DO: try and generalize this treatment (applicable for SLOT-PERFORMANCE2 at the moment) | |
454 | % i.e. also treat (x,y):AVL & OTHERPRED not just in CLPFD off or data validation mode | |
455 | % see e.g. test 1162 with predicate: | |
456 | % eq1 |-> et |-> es : #5775:{(0|->0|->97),,...,(524|->10|->-1)} & es /= -1 & er |-> es : #1497:{(0|->97),...,(1496|->1959)} & RANGE_LAMBDA__ = eq1 |-> er | |
457 | find_useless_enumeration_id(member(LHS,b(value(V),_,_)),UselessID) :- | |
458 | % we enumerate the identifier using memberships of avl_sets which will be more precise than naive enumeration | |
459 | nonvar(V), V=avl_set(_), | |
460 | extract_useless(LHS,UselessID). | |
461 | find_useless_enumeration_id(conjunct(TA,TB),UselessID) :- | |
462 | (get_preference(use_clpfd_solver,false) -> true | |
463 | ; get_preference(data_validation_mode,true)), | |
464 | % only look deeper if CLPFD is off; useful for test 1945, 1162 | |
465 | % in general it could be better to enumerate a variable if it occurs in multiple sets and we using CLPFD intersection we can narrow down the interval | |
466 | % e.g., x: 1..100 & x:99..1000 --> we can narrow down x to 99..100 and it would be better to enumerate x | |
467 | (get_texpr_expr(TA,P) ; get_texpr_expr(TB,P)), | |
468 | find_useless_enumeration_id(P,UselessID). | |
469 | ||
470 | extract_useless(b(couple(A,B),_,_),UselessID) :- | |
471 | (extract_useless(A,UselessID) ; extract_useless(B,UselessID)). | |
472 | extract_useless(b(identifier(UselessID),_,_),UselessID) :- | |
473 | % the identifier is bound by the member test; we do not need to enumerate it | |
474 | debug_println(9,not_enum_inside_closure(UselessID)). | |
475 | ||
476 | % ------------------------------- | |
477 | ||
478 | ||
479 | :- load_files(library(system), [when(compile_time), imports([environ/2])]). | |
480 | :- if(environ(prob_debug_watch_flag,true)). | |
481 | :- dynamic reached/4, enumerated/2,enum_edge/3. | |
482 | check_reached(_Val,_Type,_VarID,_Card,_WF) :- check_reached(_Val,_Type,_VarID,_Card,enum,_WF). | |
483 | check_reached(_Val,_Type,VarID,_Card,Info,_WF) :- | |
484 | (retract(reached(VarID,RR,Info)) -> true ; RR=0), | |
485 | R1 is RR+1, | |
486 | assertz(reached(VarID,R1,Info)), | |
487 | (enumerated(TopVar,Level) -> add_edge(TopVar,VarID,Level),L1 is Level+1 ; L1=0), | |
488 | asserta(enumerated(VarID,L1)). | |
489 | check_reached(_Val,_Type,VarID,_Card,_Info,_WF) :- | |
490 | retract(enumerated(VarID,_)),fail. | |
491 | add_edge(TopVar,VarID,Level) :- | |
492 | (enum_edge(TopVar,VarID,Level) -> true ; assertz(enum_edge(TopVar,VarID,Level))). | |
493 | ||
494 | % b_enumerate:print_enum_stats. | |
495 | print_enum_stats :- findall( enum(Count,VarID,Info), reached(VarID,Count,Info),L), | |
496 | sort(L,SL), | |
497 | print('Enumeration Frequency'),nl, | |
498 | member(enum(C,ID,I),SL), format(' ~w ~w ~w~n',[C, ID, I]), | |
499 | fail. | |
500 | print_enum_stats :- | |
501 | enum_edge(TopVar,VarID,0), format('~w ---> ~w~n',[TopVar,VarID]), | |
502 | print_edges(VarID,1), | |
503 | fail. | |
504 | print_enum_stats :- nl. | |
505 | print_edges(VarID,Level) :- enum_edge(VarID,NxtId,Level), | |
506 | format(' ~w -(~w)--> ~w~n',[VarID,Level,NxtId]), | |
507 | fail. | |
508 | print_edges(_,_). | |
509 | :- else. | |
510 | check_reached(Val,Type,Var,Card,_WF) :- is_lambda_result_id(Var), | |
511 | Card \= det, \+ ground_value(Val), !, | |
512 | format('~n*** Enumerating lambda result ~w : ~w (Type=~w, Card=~w)~n~n',[Var,Val,Type,Card]). | |
513 | check_reached(_Val,_Type,_VarID,_Card,_WF). % :- format('Enumerating ~w (~w) : ~w~n',[_VarID,_Card,_Val]). | |
514 | check_reached(_Val,_Type,_VarID,_Card,_Info,_WF). % :- format('Enumerating ~w (~w:~w) : ~w~n',[_VarID,_Card,_Info,_Val]). | |
515 | :- endif. | |
516 | ||
517 | /* comment in for debugging : | |
518 | % dummy call to register variable name with free Prolog variables: | |
519 | % can be obtained using frozen() | |
520 | :- public register_prolog_variable_name/3. % Debugging tool | |
521 | :- block register_prolog_variable_name(-,?,?). | |
522 | register_prolog_variable_name(int(Val),Name,Type) :- !, | |
523 | register_prolog_variable_name(Val,Name,Type). | |
524 | register_prolog_variable_name(fd(Val,_),Name,Type) :- !, | |
525 | register_prolog_variable_name(Val,Name,Type). | |
526 | register_prolog_variable_name(string(Val),Name,Type) :- !, | |
527 | register_prolog_variable_name(Val,Name,Type). | |
528 | register_prolog_variable_name((A,B),Name,Type) :- !, | |
529 | register_prolog_variable_name(A,left(Name,mapto(B)),Type), | |
530 | register_prolog_variable_name(B,right(Name,mapfrom(A)),Type). | |
531 | register_prolog_variable_name([A|T],Name,Type) :- !, | |
532 | register_prolog_variable_name(A,el(Name),Type), | |
533 | register_prolog_variable_name(T,Name,Type). | |
534 | register_prolog_variable_name(_,_,_). | |
535 | % TO DO: something that pretty prints the path information for the average user | |
536 | ||
537 | % a tool to get variable name if it was registered | |
538 | :- public get_prolog_variable_name/3. % Debugging tool | |
539 | get_prolog_variable_name(X,Name,Type) :- var(X), | |
540 | frozen(X,Goal), print(goal(X,Goal)),nl, | |
541 | goal_member(Goal,b_enumerate:register_prolog_variable_name(_,ID,T)), | |
542 | Name=ID, Type=T. | |
543 | goal_member(X,X). | |
544 | goal_member((A,B),X) :- goal_member(A,X) ; goal_member(B,X). | |
545 | ------- */ | |
546 | ||
547 | sort_typed_values([],R,_,_,R). | |
548 | sort_typed_values([typedval(Val,Type,VarID,EnumWarningInfo)|Rest],SortedSoFar,UselessIds,DelayedIds,Res) :- | |
549 | %% comment in next line to keep track of variable names for enumeration warnings, etc... | |
550 | %% register_prolog_variable_name(Val,VarID,Type), % dummy call to register variable name with free Prolog variables | |
551 | (is_lambda_result_or_useless(VarID,EnumWarningInfo,UselessIds) -> % DO_NOT_ENUMERATE | |
552 | (%Rest=[],SortedSoFar=[], % we used to check it is the only non-ground value to enumerate | |
553 | \+ ground_value(Val) | |
554 | % could be relevant if a variable was annotated as do_not_enumerate and there is a WD error in | |
555 | % the expression defining it, e.g. in test 2337: {id19,id20|id19 : BOOL & id20 <: INTEGER}(id1) = x | |
556 | -> % Note: we can have a construct {lambda_result| #x.( P(x) & lambda_result=E(x))}, | |
557 | % we want to enumerate it after trying to solve the existential quantifier | |
558 | last_priority(Prio), % important that it is higher than existential quantifier prio, cf test 1492 | |
559 | add_enum_warning_info(EnumWarningInfo,NewInfo), | |
560 | insert_val(SortedSoFar,Val,Type,VarID,NewInfo,Prio,NewSorted), | |
561 | (debug:debug_level_active_for(9), | |
562 | Rest\=[], SortedSoFar \= [] % then it is the only variable anyway | |
563 | -> format('~n*** Possibly enumerating lambda result ~w : ~w (~w:~w)~n~n',[VarID,Val,Type,Prio]) | |
564 | ; true) | |
565 | % was triggered in test 1093 and 1161, but seems no longer necessary there | |
566 | ; NewSorted=SortedSoFar % completely skip enumeration | |
567 | ) | |
568 | ; ground_value(Val) -> NewSorted=SortedSoFar | |
569 | ; get_max_cardinality_as_priority(Type,CardPrio), | |
570 | (member(delay_enum(PosNr,VarID),DelayedIds), | |
571 | integer_pow2_priority(Prio), P2 is Prio+PosNr, | |
572 | P2>CardPrio | |
573 | -> debug_println(9,'DELAY_ENUMERATION'(VarID,PosNr,P2,CardPrio)), | |
574 | get_bounded_priority(P2,EnumPrio) | |
575 | ; EnumPrio=CardPrio), | |
576 | %% TO DO: maybe also take shape of Val into account; e.g. if cardinality of a set already fixed... | |
577 | insert_val(SortedSoFar,Val,Type,VarID,EnumWarningInfo,EnumPrio,NewSorted) | |
578 | ), | |
579 | sort_typed_values(Rest,NewSorted,UselessIds,DelayedIds,Res). | |
580 | ||
581 | get_max_cardinality_as_priority(integer,Prio) :- !, integer_priority(Prio). | |
582 | get_max_cardinality_as_priority(Type,Prio) :- | |
583 | ? | max_cardinality(Type,Card), !, |
584 | (number(Card) % \= inf or inf_overflow | |
585 | -> %Prio=Card | |
586 | get_bounded_priority(Card,Prio) | |
587 | ; inf_type_prio(Type,Prio) | |
588 | ). | |
589 | get_max_cardinality_as_priority(Type,Prio) :- | |
590 | add_internal_error('Failed: ',max_cardinality(Type,_)), | |
591 | integer_priority(Prio). | |
592 | ||
593 | % Try to give priority to simpler infinite types (such as integer over seq(_) ,...) | |
594 | % TO DO: to a full-blown analysis ... | |
595 | inf_type_prio(integer,Prio) :- !, integer_priority(Prio). | |
596 | inf_type_prio(real,Prio) :- !, integer_priority(P1), Prio is P1+11. | |
597 | inf_type_prio(seq(X),Prio) :- !, inf_type_prio(X,P1), Prio is P1+11. | |
598 | inf_type_prio(set(X),Prio) :- !, inf_type_prio(X,P1), Prio is P1+10. | |
599 | %inf_type_prio(couple(_,_),Prio) :- !, TO DO check if both args are infinite or not ... | |
600 | inf_type_prio(_,Prio) :- !, integer_priority(P), Prio is P+1. | |
601 | ||
602 | :- public portray_typed_values/1. | |
603 | portray_typed_values(List) :- maplist(print_typedvalc,List),nl. | |
604 | print_typedvalc(typedvalc(_Val,_Type,VarID,_EnumWarning,Card)) :- | |
605 | format('~w (card:~w) ',[VarID,Card]). | |
606 | ||
607 | %typedvalc = typedval + cardinality information | |
608 | insert_val([],Val,Type,VarID,EnumWarning,Card,[typedvalc(Val,Type,VarID,EnumWarning,Card)]). | |
609 | insert_val([TC|T], Val,Type, VarID, EnumWarning, Card, Res) :- | |
610 | TC = typedvalc(V1,_T1,_Var1,_W1,C1), | |
611 | ? | (cardval_greater_than(C1,V1,Card,Val) |
612 | -> Res = [typedvalc(Val,Type,VarID,EnumWarning,Card),TC|T] | |
613 | ; Res = [TC|RT], | |
614 | insert_val(T,Val,Type,VarID,EnumWarning,Card,RT) | |
615 | ). | |
616 | ||
617 | :- use_module(inf_arith,[infgreater/2]). | |
618 | :- use_module(library(random)). | |
619 | cardval_greater_than(C1,_,Card,_) :- infgreater(C1,Card). %Card \== inf, (C1=inf -> true ; C1>Card). | |
620 | cardval_greater_than(Card,V1,Card,Val) :- | |
621 | var(V1), nonvar(Val). % if card equal give priority to nonvariable values | |
622 | cardval_greater_than(Card,_,Card,_) :- | |
623 | preferences:preference(randomise_enumeration_order,true), | |
624 | % TO DO: maybe we even want to change the order if cardinality different ? | |
625 | % this also does not really generate random permutation of variables with same cardinality | |
626 | random(1,3,1). | |
627 | ||
628 | /* | |
629 | % call to enumerate an integer ensuring that you go at least from MinFrom to MinTo | |
630 | %:- block enumerate_integer_with_min_range_wf(-,?,?,?). | |
631 | enumerate_integer_with_min_range_wf(int(X),MinFrom,MinTo,WF) :- | |
632 | integer_priority(Prio), P1 is Prio-1, | |
633 | ... removed ... | |
634 | */ |