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
6 :- module(kernel_waitflags,
7 [init_wait_flags/1, init_wait_flags/2,
8 waitflag0_is_set/1,
9 get_wait_flag_infos/2, get_wait_flag_info/2, is_wait_flag_info/2,
10 add_wait_flag_info/3, portray_wait_flag_infos/1,
11 push_wait_flag_call_stack_info/3, opt_push_wait_flag_call_stack_info/3, debug_opt_push_wait_flag_call_stack_info/3,
12 opt_push_wait_flag_call_stack_quantifier_info/6,
13 copy_wait_flag_call_stack_info/3,
14 init_quantifier_wait_flag/6,
15 init_wait_flags_and_push_call_stack/3, init_wait_flags_and_push_call_stack/4,
16 init_wait_flags_with_call_stack/2,
17 add_call_stack_to_span/3,
18 add_error_wf/5, add_warning_wf/5, add_message_wf/5, add_internal_error_wf/5,
19 add_state_error_wf/5,
20 portray_call_stack/1, get_call_stack/2,
21 get_wait_flag/3, get_wait_flag/4,
22 get_bounded_wait_flag/4, get_bounded_priority/2,
23 block_get_wait_flag/4,
24 get_wait_flag0/2,
25 get_wait_flag1/2, get_wait_flag1/3, % deterministic wait flag
26 get_last_wait_flag/3,
27 get_idle_wait_flag/3,
28 get_binary_choice_wait_flag/3,
29 get_pow2_binary_choice_priority/2,
30 get_binary_choice_wait_flag_exp_backoff/3, get_binary_choice_wait_flag_exp_backoff/4,
31 %refresh_wait_flag/4,
32 add_fd_variable_for_labeling/3, add_fd_variable_for_labeling/2, add_fd_variables_for_labeling/2,
33 get_new_subsidiary_wait_flag/4,
34 update_waitflag/4,
35 ground_constraintprop_wait_flags/1,
36 check_is_wait_flag/1,
37 no_pending_waitflags/1,
38 create_inner_wait_flags/3, copy_wf01e_wait_flags/2,
39 copy_wfe_from_inner_if_necessary/2,
40 %copy_wf_start/2,
41 copy_wf_start/3,
42 copy_wf_finish/2,
43 copy_waitflag_store/2,
44
45 clone_wait_flags_from1/3, clone_wait_flags_from1_finish/3,
46 ground_wait_flag0/1,
47 %create_wdguarded_wait_flags/4,
48 get_large_finite_wait_flag/3,
49 get_enumeration_starting_wait_flag/3,
50 %get_enumeration_almost_finished_wait_flag/3,
51 get_enumeration_finished_wait_flag/2, % get_wfe
52 grd_ef/1, % ground enumeration finished waitflag
53 get_integer_enumeration_wait_flag/3,
54 ground_det_wait_flag/1, ground_wait_flag_to/2,
55 ground_wait_flags/1, ground_inner_wait_flags/1,
56 ground_inner_wait_flags_in_context/2,
57 start_attach_inner_abort_errors/2, re_attach_pending_inner_abort_errors/3,
58 deref_wf/2,
59
60 add_abort_error_span/5,
61 add_wd_error_span/4,
62 add_wd_error/3, add_wd_error_set_result/6,
63 pending_abort_error/1, pending_abort_error/4,
64 get_wait_flags_context_msg/2,
65 add_wd_error_span_now/4,
66 assert_must_abort_wf/2,
67
68 get_minimum_waitflag_prio/3,
69 portray_waitflags/1, quick_portray_waitflags/1, quick_portray_waitflags_store/1,
70 get_waitflags_phase/2,
71 find_waitflag_info/4,
72
73 integer_priority/1, large_finite_priority/1, integer_pow2_priority/1, last_priority/1,
74
75 my_get_kodkod_predicates/3, my_add_predicate_to_kodkod/3,
76 my_get_satsolver_predicates/3, my_add_predicate_to_satsolver/3,
77
78 get_wf_dynamic_info/3, put_wf_dynamic_info/3, % these infos are stored together with the Kodkod preds
79 % they can always be added and do not require updating the WF term like add_wait_flag_info does
80 % add_wait_flag_info are static/lexical scoping infos; these are dynamic infos
81
82 set_silent/1]).
83 /* File: kernel_waitflags.pl */
84 /* Created: 17/3/06 by Michael Leuschel */
85
86 :- meta_predicate assert_must_abort_wf(0,*).
87 %:- meta_predicate assert_must_abort2_wf(0,-).
88
89 /*
90 Phases of the ProB Kernel:
91
92 -> all Waitflags unbound
93
94 * Phase 0: propagation of completely known ground values in a deterministic fashion
95
96 -> WF0 is bound (first to s(VAR) and then to s(0))
97 when WF0 is not ground one knows that the WF0 phase is still ongoing
98 (relevant for getting 1.0 waitflag)
99
100 * Phase 1: deterministic propagation (not necessarily of completely known full values)
101
102 -> WF1 is bound (1.0)
103
104 * Phase 2: boolean propagation (e.g., P or Q will now try P or Q)
105
106 -> WF2 is bound
107
108 * Phase 3: non-deterministic propagation (e.g, x: {1,2,3})
109
110 -> Enumeration Starting WF is bound
111
112 * Enumeration Phase: (later to be split into enumeration of finite and infinite types (e.g., integer)
113
114 -> Enumeration Finished Waitflag bound
115
116 * Error Generation Phase: errors for, e.g., division by zero, f(x) with x outside domain of f,... are generated
117 * This phase should not create choice points and should not detect failure
118 (e.g., in the
119 case of negated existential quantifiers the Enumeration Finished Waitflag will be set outside of delay_not)
120
121
122 We now distinguish
123 - Enumeration Wait Flags for Decision Variables with estimated enumeration domain size as priority
124 - Binary Choice Point Wait Flags (stemming e.g. from disjunctions)
125
126 Note: all floats like 1.0, 1.5, 2.0 ... are dealt with before all integers (even 1) !
127
128 There are three ways to create a waitflag store:
129 - init_wait_flags : to create a fresh empty store
130 - create_inner_wait_flags: the inner WFE should not be instantiated and will be instantiated from the outside
131 useful for a nested quantifier where well-definedness errors should only be raised when the outer quantifier
132 is set and where you want to enumerate the inner quantifier in isolation (e.g., for exists to be able to use the Prolog cut)
133 - copy_wf_start to create a temporary copy with WF0 unset, which will be unified with copy_wf_finish
134 useful for adding a new predicate to the constraint solver and ensure that WF0 is not set until
135 predicate has been fully interpreted, to avoid enumerations/inefficient computations (e.g., there
136 could be an obvious inconsistency at the end of the predicate)
137 */
138
139 :- use_module(error_manager).
140 :- use_module(self_check).
141 :- use_module(typechecker).
142 :- use_module(library(clpfd),[fd_size/2, fd_degree/2, (in/2)]).
143 :- use_module(tools).
144 :- use_module(tools_portability, [exists_source/1]).
145 :- use_module(covsrc(coverage_tools_annotations),['$NOT_COVERED'/1]).
146 :- use_module(preferences,[preference/2, get_preference/2]).
147 :- use_module(debug,[debug_format/3]).
148
149 :- use_module(module_information,[module_info/2]).
150 :- module_info(group,kernel).
151 :- module_info(description,'This module manages the various choice points and enumerators of ProB, by maintaining the kernel Waitflags store.').
152
153 :- type wait_flag +--> (var ; wfx(simple,mutable,simple,list(any)) ; no_wf_available).
154 % a mutable either contains:
155 % wfm_store(AVLWaitflagHeap,NewFDVARS,KodSatPredicateList)
156 % wfm_ref(Mutable) a reference pointing to another Mutable which should be used to store waitflags
157
158 :- use_module(library(lists),[is_list/1, delete/3, last/2]).
159 :- use_module(library(avl)).
160
161 % -------------- ATTRIBUTES ------------------
162
163 % Portable attributed variable handling.
164 % Use SICStus-style library(atts) and verify_attributes/3 if available,
165 % otherwise the SWI/hProlog-style attr builtins and attr_unify_hook/2.
166 :- if(\+ exists_source(library(atts))).
167
168
169 is_wf_abort_pending(Var) :- get_attr(Var,kernel_waitflags,wf_abort_pending).
170 is_not_wf_abort_pending(Var) :- \+ get_attr(Var,kernel_waitflags,wf_abort_pending).
171 put_wf_abort_pending(Var) :- put_attr(Var,kernel_waitflags,wf_abort_pending).
172
173 attr_unify_hook(wf_abort_pending, Value) :-
174 % we are unifying the EWF enumeration finished waitflag, not the store
175 (nonvar(Value) -> true % EWF bound, enumeration finished
176 ; get_attr(Value,kernel_waitflags,wf_abort_pending) -> true % other EWF Value already has same attribute
177 ; put_attr(Value,kernel_waitflags,wf_abort_pending)
178 ).
179
180 :- else.
181
182 :- use_module(library(atts)).
183 :- attribute %wf_store/1,wf_fdvars/1, wf_kodkod/2, wf_satsolver/2, % now stored in mutable
184 wf_abort_pending/0.
185
186 is_wf_abort_pending(Var) :- get_atts(Var,+wf_abort_pending).
187 is_not_wf_abort_pending(Var) :- get_atts(Var,-wf_abort_pending).
188 put_wf_abort_pending(Var) :- put_atts(Var,+wf_abort_pending).
189
190 % verify_attributes is called whenever a variable Var that might have attributes is about to be bound to Value (it might have none).
191 % verify(VariableBeforeUnification, ValueWithWhichVariableUnifies, GoalWhichSICStusShouldCallAfterUnif)
192 verify_attributes(Var, Value, [] ) :- get_atts(Var,+wf_abort_pending),!,
193 % we are unifying the EWF enumeration finished waitflag, not the store
194 (nonvar(Value) -> true % EWF bound, enumeration finished
195 ; get_atts(Value,+wf_abort_pending) -> true % other EWF Value already has same attribute
196 ; put_atts(Value,+wf_abort_pending)
197 ).
198 verify_attributes(Var, Value, [] ) :-
199 (nonvar(Value) -> print('Trying to unify WFE flags'),nl
200 ; print(verify_attributes(Var,Value)),nl).
201 % The SICStus documentation is not entirely clear: it seems we only have to copy attributes from Var to Value, not the other way around
202
203 :- endif.
204
205 % -------------- MUTABLES ------------------
206
207 % Should we use several mutables?
208 put_mutable_other_attr(Store,wf_preds(ID,Type,PredList)) :- !,
209 get_mutable_store_for_update(Store,S,FD,KodSat,StoreToUpdate),
210 update_preds(KodSat,ID,Type,PredList,KodSat2),
211 update_mutable(wfm_store(S,FD,KodSat2),StoreToUpdate).
212 put_mutable_other_attr(Store,wf_info(ID,Val)) :- !, % Store other infos in the wf_preds list
213 get_mutable_store_for_update(Store,S,FD,KodSat,StoreToUpdate),
214 (select(wf_info(ID,_),KodSat,Rest) -> true ; Rest=KodSat),
215 update_mutable(wfm_store(S,FD,[wf_info(ID,Val)|Rest]),StoreToUpdate).
216 put_mutable_other_attr(Store,New) :-
217 add_internal_error('Uncovered put_mutable_other_attr',put_mutable_other_attr(Store,New)).
218
219 % update AVL WF heap in WF Store
220 % faster version of put_mutable_attr(Store,wf_store(NewHeap))
221 put_mutable_wf_store_attr(Store,NewHeap) :-
222 get_mutable_store_for_update(Store,_,FD,KodSat,StoreToUpdate),
223 update_mutable(wfm_store(NewHeap,FD,KodSat),StoreToUpdate).
224
225 % update FD Variables List in WF Store
226 % faster version of put_mutable_attr(Store,wf_fdvars(NewFDVARS))
227 put_mutable_wf_fdvars_attr(Store,NewFDVARS) :-
228 get_mutable_store_for_update(Store,Heap,_,KodSat,StoreToUpdate),
229 update_mutable(wfm_store(Heap,NewFDVARS,KodSat),StoreToUpdate).
230
231 % TO DO: improve my_get_wf_store_fdvars_atts / put_mutable_attr pairs of calls to make them more efficient
232
233 % update Kodkod or Satsolver predicates attached in store:
234 update_preds([],ID,Type,PredList,[wf_preds(ID,Type,PredList)]).
235 update_preds([wf_preds(ID,Type,_)|T],ID,Type,PredList,Res) :- !,
236 Res = [wf_preds(ID,Type,PredList)|T].
237 update_preds([H|T],ID,Type,PredList,[H|RT]) :-
238 update_preds(T,ID,Type,PredList,RT).
239
240 % a utility predicate that always returns a mutable with wfm attribute
241 get_mutable_store_for_update(Store, S,FD,KodSat, MutableToUpdate) :- mutable(Store),!,
242 get_mutable(MutableInfo,Store),
243 (get_mutable_infos__for_update_aux(MutableInfo,S,FD,KodSat,Store,MutableToUpdate) -> true
244 ; add_internal_error('get_mutable_infos__for_update_aux failed for:',Store),fail).
245 get_mutable_store_for_update(Store, S,[],[], Res) :- var(Store),!, Res=Store,
246 empty_avl(S),
247 create_mutable(wfm_store(S,[],[]),Store).
248 get_mutable_store_for_update(Store, _,_,_, _) :-
249 add_internal_error('get_mutable_store_for_update failed for:',Store),fail.
250
251 get_mutable_infos__for_update_aux(wfm_store(S,FD,KodSat),S,FD,KodSat,Store,Store).
252 get_mutable_infos__for_update_aux(wfm_ref(OuterStore),S,FD,KodSat,_,MutableToUpdate) :-
253 get_mutable_store_for_update(OuterStore,S,FD,KodSat,MutableToUpdate).
254
255 % follow links of wfm_ref to get access to proper mutable store to update
256 deref_store(Store,MutableToUpdate) :- mutable(Store), !,
257 (Store=wfm_ref(Store2) -> deref_store(Store2,MutableToUpdate)
258 ; MutableToUpdate=Store).
259 deref_store(Store,MutableToUpdate) :- var(Store),!, MutableToUpdate=Store,
260 empty_avl(S),
261 create_mutable(wfm_store(S,[],[]),Store).
262 deref_store(Store, _) :-
263 add_internal_error('deref_store failed for:',Store),fail.
264
265 % get the associated infos for a mutable store; follow chain of wfm_ref's:
266 get_mutable_infos(S,FD,KodSat,Store) :- mutable(Store),
267 get_mutable(MutableInfo,Store),
268 get_mutable_infos_aux(MutableInfo,S,FD,KodSat).
269 get_mutable_infos_aux(wfm_store(S,FD,KodSat),S,FD,KodSat).
270 get_mutable_infos_aux(wfm_ref(OuterStore),S,FD,KodSat) :- get_mutable_infos(S,FD,KodSat,OuterStore).
271
272 my_get_wf_store_att(Store,Heap) :- get_mutable_infos(S,_,_,Store),!,
273 Heap=S.
274 my_get_wf_store_att(_,E) :- empty_avl(E).
275
276 my_get_wf_preds_att(Store,ID,Type,PredList) :- get_mutable_infos(_,_,KodSat,Store),!,
277 (member(wf_preds(ID,Type,R),KodSat) -> PredList=R).
278
279 my_get_wf_info_att(Store,ID,Value) :- get_mutable_infos(_,_,KodSat,Store),!,
280 (member(wf_info(ID,V),KodSat) -> Value=V).
281
282 my_get_all_wf_info_atts(Store,List) :- get_mutable_infos(_,_,KodSat,Store),!,
283 findall(wf_info(ID,V),member(wf_info(ID,V),KodSat),List).
284 my_get_all_wf_info_atts(_,List) :-
285 %init_wait_flags/1 will not initialise a mutable and get_mutable_infos will fail
286 List=[].
287
288 my_get_fdvars_att(Store,FDList) :- get_mutable_infos(_,FD,_,Store),!,
289 FDList=FD.
290 my_get_fdvars_att(_,[]).
291
292 my_get_wf_store_fdvars_atts(Store,Heap,FDList) :- get_mutable_infos(S,FD,_,Store),!,
293 Heap=S, FDList=FD.
294 my_get_wf_store_fdvars_atts(_,E,[]) :- empty_avl(E).
295
296 % Mutable store should now point to another mutable store (OuterStore)
297 % ensures that calls which add waitflags to Store now get-redirected to OuterStore
298 set_mutable_ref(Store,OuterStore) :- mutable(Store),
299 !,
300 update_mutable(wfm_ref(OuterStore),Store).
301 set_mutable_ref(Store,OuterStore) :- % not instantiated yet, we can simply unify
302 Store=OuterStore.
303
304
305 % -------------------------
306
307 % the Call must succeed and generate one abort_error
308 assert_must_abort_wf(M:Call,WF) :- assert_must_succeed_any(M:(kernel_waitflags:assert_must_abort2_wf(M:Call,WF))).
309
310 :- use_module(tools_printing,[format_with_colour_nl/4]).
311 assert_must_abort2_wf(_,_) :-
312 get_error(well_definedness_error,M),
313 add_internal_error('Abort error prior to call in assert_must_abort: ',M),fail.
314 assert_must_abort2_wf(Call,WF) :-
315 (real_error_occurred -> RE=true ; RE=false),
316 init_wait_flags(WF,[assert_must_abort2_wf]),
317 call(Call),
318 (ground_wait_flags(WF)
319 -> add_internal_error('Call did not fail after grounding WF: ',assert_must_abort2_wf(Call,WF)),fail
320 ; get_error(well_definedness_error,_), % we have 1 abort_error
321 format_with_colour_nl(user_output,[blue],'Got expected well-definedness error',[]),
322 (RE=false -> reset_real_error_occurred ; true)
323 ).
324
325
326 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF), kernel_waitflags:check_is_wait_flag(WF) )).
327
328 check_is_wait_flag(WFX) :- var(WFX),!,
329 add_internal_error('Variable as waitflag: ',check_is_wait_flag(WFX)).
330 check_is_wait_flag(no_wf_available) :- !.
331 check_is_wait_flag(WFX) :-
332 WFX = wfx(WF0,Store,WFE,Infos)
333 -> (get_mutable_infos(_,_,_,Store)
334 -> true
335 ; print('WF does not yet contain heap: '), print(WFX),nl),
336 %add_internal_error('WF does not contain heap: ',check_is_wait_flag(WFX)))
337 (var(WF0) -> true ; WF0 = s(WF00), \+compound(WF00) -> true
338 ; compound(WF0) -> add_internal_error('WF0 is compound: ',WF0) ; true
339 ),
340 (compound(WFE) -> add_internal_error('WFE is compound: ',WFE) ; true),
341 (nonvar(Infos),is_list(Infos) -> true ; add_internal_error('WF Infos not list: ',Infos))
342 ; add_internal_error('Illegal WF Format: ',check_is_wait_flag(WFX)).
343
344
345 % initialise a new waitflag store
346 init_wait_flags(WF,Info) :-
347 (nonvar(Info) -> true ; add_internal_error('Illegal WF Format: ',init_wait_flags(WF,Info))),
348 init_debug_wait_flag_info(Info,Infos),
349 WF=wfx(_,_Store,_,Infos).
350 init_wait_flags(wfx(_,_Store,_,[])). % no_expansion_context_available
351 init_wait_flags_with_infos(wfx(_,_Store,_,Infos),Infos). % infos copied as is without checking
352 init_wait_flags_with_sd_infos(wfx(_,Store,_,StaticInfos),StaticInfos,DynamicInfos) :-
353 init_wait_flags_store_with_dynamic_infos(Store,DynamicInfos).
354
355 %init_wait_flags(wfx(_,Store,_,[])) :- init_wait_flags_store(Store).
356 % we no longer do this because of performance, (see, e.g., LotsOfInvariants.mch) :
357 init_wait_flags_store(Store) :- empty_avl(Heap),
358 create_mutable(wfm_store(Heap,[],[]),Store).
359 init_wait_flags_store_with_dynamic_infos(Store,KodkodDynInfos) :- empty_avl(Heap),
360 create_mutable(wfm_store(Heap,[],KodkodDynInfos),Store).
361 %init_wait_flags_store(Store) :-
362 % empty_avl(Heap),
363 % put_atts(Store,wf_store(Heap)),
364 % put_atts(Store,wf_fdvars([])).
365
366
367 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF),
368 kernel_waitflags:ground_wait_flag0(WF), kernel_waitflags:waitflag0_is_set(WF) )).
369 :- assert_must_fail(( kernel_waitflags:init_wait_flags(WF), kernel_waitflags:waitflag0_is_set(WF) )).
370 waitflag0_is_set(wfx(WF0,_Store,_EF,_INFOS)) :- ground(WF0).
371 waitflag0_is_set(no_wf_available).
372
373 get_new_subsidiary_wait_flag(OldWF,Info,WFX,NewWF) :-
374 (ground(OldWF) /* then we have created a choice point with the old WF: double new WF prio */
375 -> double_prio(OldWF,NewPrio), get_wait_flag(NewPrio,Info,WFX,NewWF)
376 ; NewWF = OldWF
377 ).
378
379 double_prio(OldPrio,NewPrio) :- number(OldPrio),!, NewPrio is OldPrio*2.
380 double_prio(s(_),2) :- !. % for things like s(0) created for WF0
381 double_prio(Old,New) :- add_internal_error('Unknown priority: ',double_prio(Old,New)), New=2.
382
383 % not used at the moment:
384 %refresh_wait_flag(OldWF,Info,WFX,NewWF) :-
385 % (ground(OldWF) /* then we have created a choice point with the old WF: double new WF prio */
386 % -> get_wait_flag(OldWF,Info,WFX,NewWF)
387 % ; NewWF = OldWF
388 % ).
389
390 :- volatile silent/0.
391 :- dynamic silent/0.
392 set_silent(true) :- !,(silent -> true ; assertz(silent)).
393 set_silent(false) :- retractall(silent).
394 %waitflag_not_init :- silent -> true ; print('waitflag-store not initialised'),nl, '$NOT_COVERED'('This should not happen').
395
396 :- use_module(eventhandling,[register_event_listener/3]).
397 :- register_event_listener(start_unit_tests,set_silent(true),'Allow wf not to be set up without printing a warning').
398 :- register_event_listener(stop_unit_tests, set_silent(false), 'Printing wf warnings again').
399
400 :- use_module(library(lists),[maplist/2]).
401 add_fd_variables_for_labeling(Vars,WF) :- WF==no_wf_available,!,
402 add_internal_error('Cannot add FDVars to waitflag store: ',Vars).
403 add_fd_variables_for_labeling(Vars,WF) :-
404 WF=wfx(_,Store,_EnumFinished,_INFOS),
405 !,
406 my_get_fdvars_att(Store,FDVARS),
407 l_add_fd_var_to_FDVARS(Vars,FDVARS,NewFDVARS),
408 put_mutable_wf_fdvars_attr(Store,NewFDVARS).
409 add_fd_variables_for_labeling(Vars,WF) :-
410 add_internal_error('Illegal waitflag store: ',add_fd_variables_for_labeling(Vars,WF)).
411
412 l_add_fd_var_to_FDVARS([],Acc,Acc).
413 l_add_fd_var_to_FDVARS([Var|VT],FDVARS,NewFDVARS) :-
414 (nonvar(Var) -> F2=FDVARS; add_fd_var_to_FDVARS(Var,FDVARS,F2)),
415 l_add_fd_var_to_FDVARS(VT,F2,NewFDVARS).
416
417 % adds a CLP(FD) variable with given domain Size, will be enumerated by labeling (even if clpfd_solver preference is false !)
418 % Size could be computed by fd_size(FDVariable,Size)
419 add_fd_variable_for_labeling(FDVariable,WF) :-
420 add_fd_variable_for_labeling_aux(FDVariable,WF).
421 add_fd_variable_for_labeling(FDVariable,Size,WF) :- Size = not_used_anymore,
422 add_fd_variable_for_labeling_aux(FDVariable,WF).
423
424
425 add_fd_variable_for_labeling_aux(FDVariable,_WF) :- nonvar(FDVariable),!.
426 add_fd_variable_for_labeling_aux(FDVariable,WF) :-
427 % otherwise: store the FDVariable in the separate list of FD Variables (all mixed together)
428 WF=wfx(_,Store,_EnumFinished,_),
429 my_get_fdvars_att(Store,FDVARS),
430 add_fd_var_to_FDVARS(FDVariable,FDVARS,NewFDVARS),
431 %% fd_size(FDVariable,Size),print(added(FDVariable,Size,NewFDVARS,old(FDVARS))),nl,
432 %% print(added_fd_variable(FDVariable,_Size,NewFDVARS)),nl,portray_waitflags_store(Store),nl,nl,%%
433 put_mutable_wf_fdvars_attr(Store,NewFDVARS).
434
435 add_fd_var_to_FDVARS(FDVariable,FDVARS,NewFDVARS) :-
436 %print_term_summary(add_fd_var_to_FDVARS(FDVariable)),nl,
437 %(fd_size(FDVariable,Sz), Sz=sup -> trace ; true),
438 (FDVARS=[HH|_],HH==FDVariable -> NewFDVARS = FDVARS % variable already in list [cheap check at front]
439 ; NewFDVARS = [FDVariable|FDVARS]). %% this adds new variables at front; will be given priority over older ones
440 %% add_fd_var(FDVARS,FDVariable,NewFDVARS), %% this adds new variables at the end and does more thorough duplicate check
441
442
443 % a bit like append: but cleans up list: removes numbers + checks if var already in list
444 % runtimes for tests: 349 stays at 220ms, test 1088 4660 -> 5040ms
445 %add_fd_var([],Var,[Var]).
446 %add_fd_var([V|T],Var,Res) :-
447 % (V==Var -> Res = [V|T] %,print(dup(V)),nl,
448 % ; nonvar(V) -> add_fd_var(T,Var,Res) %,print(nonvar(V)),nl
449 % ; Res = [V|RT], add_fd_var(T,Var,RT)).
450
451
452 :- use_module(library(random),[random_select/3]).
453 % reverse the list at each step; alternating taking from end and front; makes no sense with randomise_enumeration_order
454 %my_get_next_fdvar_to_enumerate_rev(FDVARS,NextFDVar,RemainingFDVARS) :- lists:reverse(FDVARS,RevFDVARS),
455 % my_get_next_fdvar_to_enumerate(RevFDVARS,NextFDVar,RemainingFDVARS).
456
457 my_get_next_fdvar_to_enumerate([],_NextFDVar,_RemainingFDVARS) :- !,fail.
458 my_get_next_fdvar_to_enumerate([V1|FDVARS],NextFDVar,RemainingFDVARS) :-
459 nonvar(V1),
460 !, % skip over non-variables (numbers); clpfd_get_next_variable_to_label does not delete variable from list and may crash with leading non-variable terms !
461 my_get_next_fdvar_to_enumerate(FDVARS,NextFDVar,RemainingFDVARS).
462 my_get_next_fdvar_to_enumerate(FDVARS,NextFDVar,RemainingFDVARS) :-
463 preferences:preference(randomise_enumeration_order,true),
464 !,
465 get_min_fd_size_elements(FDVARS,MinFDVars),
466 random_select(NextFDVar,MinFDVars,_),
467 %print(random_enum(NextFDVar,FDVARS,MinFDVars)),nl,
468 RemainingFDVARS=FDVARS. % still contains variable; but no problem as it will be discarded later
469 my_get_next_fdvar_to_enumerate(FDVARS,NextFDVar,RemainingFDVARS) :-
470 %clpfd_interface:clpfd_get_next_variable_to_label_ffc(FDVARS,NextFDVar,RemainingFDVARS).
471 clpfd_interface:clpfd_get_next_variable_to_label(FDVARS,NextFDVar),
472 % print(enumerating(NextFDVar,FDVARS)),nl, maplist(tools_printing:print_arg,FDVARS),nl,
473 RemainingFDVARS=FDVARS.
474 % ((FDVARS=[H|T],H==NextFDVar) -> RemainingFDVARS=T ; RemainingFDVARS=FDVARS).% does not seem to buy any speed
475
476 get_min_fd_size_elements([V1|FDVARS],MinFDVars) :- clpfd_size(V1,Size),
477 get_min_aux(FDVARS,Size,[V1],MinFDVars).
478
479 get_min_aux([],_,MinFDVars,MinFDVars).
480 get_min_aux([V1|FDVARS],Size,MAcc,MinFDVars) :- number(V1),!,
481 get_min_aux(FDVARS,Size,MAcc,MinFDVars).
482 get_min_aux([V1|FDVARS],Size,MAcc,MinFDVars) :-
483 clpfd_size(V1,Size1),
484 (lt_size(Size1,Size)
485 -> get_min_aux(FDVARS,Size1,[V1],MinFDVars)
486 ; Size1=Size -> get_min_aux(FDVARS,Size,[V1|MAcc],MinFDVars)
487 ; get_min_aux(FDVARS,Size,MAcc,MinFDVars)
488 ).
489
490 lt_size(X,Y) :- number(X),(Y=sup -> true ; X<Y).
491
492 % -------------
493
494 % using this is relevant for e.g. tests 415, 416, 1096 : to give variables without frozen goals a lower priority
495 % TO DO: we could filter out those before we call clpfd_get_next_variable_to_label !?
496 fd_priority_leq_limit(FDVAR,Prio,Limit) :-
497 clpfd_size(FDVAR,Size),
498 number(Size), % not =sup or =inf
499 geq_limit(Limit,Size), % the calculation below will only increase the Size
500 (Size < 2 -> Prio = Size
501 ; geq_limit(Limit,Size*4) -> Prio = Size % avoid calling frozen and fd_degree, Priority only used for warnings and bisect decisions
502 ; (enumeration_only_fdvar(FDVAR) -> Prio is Size*4, Prio =< Limit
503 ; Prio = Size)
504 ).
505
506 gt_limit(inf,_) :- !.
507 gt_limit(inf_overflow,_) :- !.
508 gt_limit(sup,_) :- !.
509 gt_limit(X,Limit) :- X > Limit.
510
511 geq_limit(inf,_) :- !.
512 geq_limit(inf_overflow,_) :- !.
513 geq_limit(sup,_) :- !.
514 geq_limit(X,Limit) :- X >= Limit.
515
516
517
518 % -------------
519
520
521 enumeration_only_fdvar(FDVAR) :- var(FDVAR),fd_degree(FDVAR,D),!,
522 D=0,
523 frozen(FDVAR,Goal), % print(frozen(FDVAR,D,Goal)),nl,
524 enum_only_frozen_goal(Goal,FDVAR).
525 enumeration_only_fdvar(_).
526
527 % TO DO: maybe use enumeration_only_goal in b_enumerate
528 enum_only_frozen_goal((A,B),Var) :- !, enum_only_frozen_goal(A,Var), enum_only_frozen_goal(B,Var).
529 enum_only_frozen_goal(true,_).
530 enum_only_frozen_goal(Module:Call,Var) :- enum_only_frozen_goal_m(Module,Call,Var).
531
532 enum_only_frozen_goal_m(clpfd,_,_) :- !.
533 enum_only_frozen_goal_m(kernel_objects,G,V) :- !, enum_only_frozen_goal_k_obj(G,V).
534 enum_only_frozen_goal_m(b_interpreter_components,observe_variable_block(_,_,_,_,_),_).
535 enum_only_frozen_goal_m(b_interpreter_components,observe_variable1_block(_,_,_,_),_).
536 enum_only_frozen_goal_m(custom_explicit_sets,block_copy_waitflag_store(_,_,_,_,_),_).
537
538 enum_only_frozen_goal_k_obj(safe_less_than_equal(_,X,Y),_) :- (number(X);number(Y)), !.
539 enum_only_frozen_goal_k_obj(call_enumerate_int(X,_,_,_),Var) :- Var==X,!.
540 enum_only_frozen_goal_k_obj(enumerate_int_wf(X,_,_,_,_),Var) :- Var==X,!.
541 enum_only_frozen_goal_k_obj(true,_).
542
543
544 % get a wait-flag that will be triggered first next time;
545 % can be used to ensure that all pending co-routines complete
546 get_idle_wait_flag(Info,wfx(WF0,Store,EnumFinished,_),LWF) :- !,
547 (var(WF0) -> LWF=WF0
548 ; WF0=s(WF00), var(WF00) -> LWF=WF00
549 ; nonvar(EnumFinished) -> LWF=1 % waitflag store already grounded to completion; nobody may drive it anymore
550 ; get_waitflag_from_store(0.9,Info,Store,LWF)
551 ).
552 get_idle_wait_flag(_Info,no_wf_available,1).
553
554
555 get_wait_flag(Prio,Store,WF) :- get_wait_flag(Prio,'??',Store,WF).
556 get_wait_flag(Prio,_Info,wfx(WF0,_Store,EnumFinished,_),WF) :- (Prio=0 ; ground(EnumFinished)),
557 !, % if EnumFinished is ground nothing will drive the Waitflag store anymore
558 % WF0 can be set to a variable by clone_wait_flags_from1
559 WF=WF0.
560 get_wait_flag(1.0,Info,wfx(WF0,Store,_EnumFinished,_),WF) :-
561 % WF for deterministic computations, not guaranteed to generate efficient representations
562 % (those are triggered in WF0 phase)
563 !,
564 %used to b: (var(WF0) -> get_waitflag_from_store(1.0,Info,Store,WF) ; WF=1).
565 (ground(WF0) % if WF0 is ground the WF0 phase is completed fully;
566 -> WF=1 % we can return this waitflag immediately;
567 % good idea also if we are already in the later stages of propagation
568 % so as not to delay the attached deterministic propagations (see test 383, SudokuHexAsConstant.mch)
569 ; get_waitflag_from_store(1.0,Info,Store,WF)).
570 get_wait_flag(Prio,Info,wfx(_,Store,EnumFinished,_),WF) :- !,
571 (ground(EnumFinished) -> WF=Prio /* enumeration has finished: return a ground WF */
572 ; get_waitflag_from_store(Prio,Info,Store,WF)).
573 get_wait_flag(Prio,Info,Store,WF) :- check_is_wait_flag(Store),
574 (Store=no_wf_available -> WF=Prio ;
575 Store=none -> print(get_wait_flag_deprecated(Prio,Info,Store,WF)),nl, WF=Prio ;
576 add_internal_error('Illegal call: ',get_wait_flag(Prio,Info,Store,WF))).
577
578 :- use_module(kernel_card_arithmetic,[is_inf_or_overflow_card/1]).
579 % get Waitflag with Priority Prio, additional information Info from Store
580 get_waitflag_from_store(Prio,Info,Store,WF) :-
581 % print(get_waitflag_from_store(Prio,Info,Store,WF)),nl,
582 (is_inf_or_overflow_card(Prio) -> integer_priority(RealPrio)
583 ; RealPrio=Prio),
584 my_get_wf_store_att(Store,Heap),
585 (avl_fetch(RealPrio,Heap,wf(WFs,_OldInfo)) %,print(fetch(RealPrio,WFs,_OldInfo)),nl
586 -> ((RealPrio < 2 %is_finite_priority(RealPrio)
587 ; Info = allow_reuse(_))
588 %RealPrio < 100000 % TO DO: investigate performance if reduce the threshold
589 % here we re-use the waitflag instead of storing a new one
590 -> (var(WFs) -> WF1 = WFs %, print(reusing_wf(RealPrio,WF1,Info)),nl
591 ; WFs = [WF1|_]-_ -> true % add_wf has constructed a difference list
592 ; print(wf_already_grounded(Prio,Info,WFs)),nl,
593 WFs = WF1
594 )
595 ; push_waitflag(WFs,WF1,WFs2) ->
596 % poses problems for 1003, possibly 1194; keep WFs as a FIFO stack to have localised enumeration
597 %(RealPrio<100000 -> test_runner:inc_test_target_coverage ; true),
598 avl_store(RealPrio,Heap,wf(WFs2,Info),NewHeap),
599 put_mutable_wf_store_attr(Store,NewHeap)
600 ; add_internal_error('Pushing waitflag failed:',RealPrio:WFs)
601 )
602 ; avl_store(RealPrio,Heap,wf(WF1,Info),NewHeap), % print(storing_fresh_wf(RealPrio,WF1,Info)),nl,
603 put_mutable_wf_store_attr(Store,NewHeap) % put_atts seems to be expensive
604 ),!,
605 WF=WF1.
606 get_waitflag_from_store(Prio,Info,Store,WF) :-
607 add_internal_error('Error getting Waitflag: ', get_waitflag_from_store(Prio,Info,Store,WF)),fail.
608
609 % ----------------
610
611 % utilities for managing difference lists of waitflag variables for a particular priority:
612
613 % for each priority we store wf(WFQueue,Info)
614 % WFQueue is either a free variable (aka waitflag), or a difference list of free variables (waitflags)
615
616 % push new waitflag NextWF onto an existing WF list for a priority
617 push_waitflag(WFs,NextWF,NewWFs) :- var(WFs),!, NewWFs = [WFs,NextWF|TAIL]-TAIL. % create difference list
618 push_waitflag(Head-TAIL,NextWF,NewWFs) :- !, TAIL = [NextWF|NewTAIL], NewWFs = Head-NewTAIL.
619 push_waitflag(WFs,_,WFs) :- add_internal_error('Illegal waitflag list:',WFs).
620
621 % pop the next waitflag NextWF from a list of waitflags associated with some priority
622 pop_waitflag(WFs,NextWF,Tail) :- var(WFs),!, NextWF=WFs,Tail=[].
623 pop_waitflag(Head-Tail,NextWF,NextTail) :- Head \== Tail, % should always succeed
624 !,
625 Head = [NextWF|NextHead],
626 (NextHead==Tail -> NextTail=[] ; NextTail = NextHead-Tail).
627 pop_waitflag(WFs,_,_) :- add_internal_error('Illegal waitflag list, cannot pop:',WFs),fail.
628
629 % merge_waitflag_queues(InnerWF,OuterWF,MergedWF)
630 merge_waitflag_queues(WF1,WFs,MergedWFs) :- var(WFs),!,
631 push_waitflag(WF1,WFs,MergedWFs).
632 merge_waitflag_queues(WF1,Head2-Tail2,MergedWFs) :- var(WF1),!,
633 MergedWFs = [WF1|Head2]-Tail2. % add WF1 at front
634 merge_waitflag_queues(Head1-Tail1,Head2-Tail2,MergedWFs) :- !, % concatenation of difference lists
635 Tail1=Head2,
636 MergedWFs = Head1-Tail2.
637 merge_waitflag_queues(WF1,WFs,MergedWFs) :-
638 add_internal_error('Illegal WF list:',merge_waitflag_queues(WF1,WFs,MergedWFs)),
639 MergedWFs = WFs.
640
641
642 % ------------------------
643
644 % a version which delays getting a waitflag until the Prio is known:
645 :- block block_get_wait_flag(-,?,?,?).
646 block_get_wait_flag(Prio,Info,WFX,WF) :- get_wait_flag(Prio,Info,WFX,WF).
647
648 % check if there are no waitflags and no FD variables; note: there could be pending co-routines on WF0
649 no_pending_waitflags(no_wf_available).
650 no_pending_waitflags(wfx(_WF0,Store,_WFE,_)) :-
651 my_get_wf_store_fdvars_atts(Store,Heap,FDList),
652 FDList=[], % no FD Variables
653 empty_avl(Heap). % no waitflags pending
654 % Note grounding could still be in progress
655 % TO DO: we could add attribute before grounding and remove after grounding !?
656
657
658 % -------------------
659
660 % storing Predicates for Kodkod external function:
661 my_get_kodkod_predicates(wfx(_,Store,_,_),ID,PredicateList) :- my_get_wf_preds(Store,ID,kodod,PredicateList).
662 my_get_wf_preds(Store,ID,Type,PredicateList) :-
663 (my_get_wf_preds_att(Store,ID,Type,PredicateList)
664 -> true
665 ; PredicateList = []
666 ).
667 %my_store_wf_kodkod(Store,ID,PredicateList) :- put_atts(Store,+wf_kodkod(ID,PredicateList)).
668 my_add_predicate_to_kodkod(wfx(_,Store,_,_),ID,Predicate) :-
669 my_add_predicate_aux(wfx(_,Store,_,_),ID,kodod,Predicate).
670 my_add_predicate_aux(wfx(_,Store,_,_),ID,Type,Predicate) :-
671 my_get_wf_preds(Store,ID,Type,PredicateList),
672 %length(PredicateList,Len), format('Adding pred to ~w of len ~w~n',[ID,Len]),
673 put_mutable_other_attr(Store,wf_preds(ID,Type,[Predicate|PredicateList])).
674
675 % -------------------
676
677 % storing Predicates for external function calling satsolver:
678 my_get_satsolver_predicates(wfx(_,Store,_,_),ID,PredicateList) :-
679 my_get_wf_preds(Store,ID,satsolver,PredicateList).
680 my_add_predicate_to_satsolver(wfx(_,Store,_,_),ID,Predicate) :-
681 my_add_predicate_aux(wfx(_,Store,_,_),ID,satsolver,Predicate).
682
683 % -------------------
684
685 % allow to store and update other information entries
686 % they are mixed in with the Kodkod predicate list
687
688 get_wf_all_dynamic_infos(no_wf_available,[]).
689 get_wf_all_dynamic_infos(wfx(_,Store,_,_),List) :- my_get_all_wf_info_atts(Store,List).
690
691 get_wf_dynamic_info(wfx(_,Store,_,_),ID,Val) :- my_get_wf_info_att(Store,ID,Val).
692
693 put_wf_dynamic_info(no_wf_available,_,_).
694 put_wf_dynamic_info(wfx(_,Store,_,_),ID,Val) :- put_mutable_other_attr(Store,wf_info(ID,Val)).
695
696 % -------------------
697
698
699 ground_wait_flag0(wfx(WF0,_,_,_)) :- grd_wf0(WF0).
700
701 ground_wait_flags(wfx(WF0,Store,EnumFinish,WFInfos)) :- !,
702 ? grd_wf0(WF0),
703 deref_store(Store,DStore),
704 init_wf_infos_for_grounding(WFInfos,WFInfos2),
705 %nl,print(' ground_wait_flags:'),nl,portray_waitflags_store(Store), portray_call_stack(wfx(WF0,Store,EnumFinish,WFInfos)),nl,
706 ? ground_waitflags_store_clpfd(DStore,WFInfos2),
707 grd_ef(EnumFinish).
708 ground_wait_flags(no_wf_available) :- !.
709 ground_wait_flags(W) :-
710 add_internal_error('Waitflags in wrong format: ',ground_wait_flags(W)).
711
712 grd_wf0(E) :- %print(start_grd_wf0(E)),nl,
713 (var(E) ->
714 E=s(E2), % first make nonvar, and then ground (separate call to avoid merging unifications)
715 ? grd_wf01(E2) %%,print(grd_wf0_done),nl
716 ; E=s(E2) -> (nonvar(E2),E2 \== 0 -> add_internal_error('Illegal WF0 Waitflag value: ',grd_wf0(E))
717 ; grd_wf01(E2))
718 ; add_internal_error('Illegal WF0 Waitflag value functor: ',grd_wf0(E))
719 ).
720 grd_wf01(0).
721
722 grd_ef(E) :- var(E) -> E=0
723 ; ((E==0;E==wd_guarded) -> true ; add_internal_error('Illegal EF Waitflag value: ',grd_ef(E))).
724
725 /* currently unused:
726 create_wdguarded_wait_flags(wfx(WF0,Store,EnumFinish,Info),Res,Expected,
727 wfx(WF0,Store,LocalEnumFinish,Info)) :-
728 %% if Res=Expected then the computations of the inner waitflags should be well-defined;
729 %% otherwise they may not
730 copy_wfe(EnumFinish,Res,Expected,LocalEnumFinish).
731 :- block copy_wfe(-,?,?,?), copy_wfe(?,-,?,?).
732 copy_wfe(EnumFinish,Res,Expected,LocalEnumFinish) :-
733 (Res==Expected -> LocalEnumFinish=EnumFinish
734 ; LocalEnumFinish=wd_guarded %% branch is pruned by well-definedness condition
735 ).
736 */
737
738 % ground_constraintprop_wait_flags will not ground WFE flag
739
740 :- load_files(library(system), [when(compile_time), imports([environ/2])]).
741 :- if(environ(prob_safe_mode,true)).
742 ground_constraintprop_wait_flags(W) :- check_is_wait_flag(W),fail.
743 :- endif.
744 ground_constraintprop_wait_flags(wfx(WF0,Store,_WFE,WFInfos)) :-
745 %% print(ground_constraintprop_wait_flags(WF0,Store,_WFE)),nl, %%
746 ? !,grd_wf0(WF0),% print(finished_grounding_wf0),nl,portray_waitflags_store(Store),
747 deref_store(Store,DStore),
748 init_wf_infos_for_grounding(WFInfos,WFInfos2),
749 ? ground_waitflags_store_clpfd(DStore,WFInfos2).
750 ground_constraintprop_wait_flags(no_wf_available) :- !.
751 ground_constraintprop_wait_flags(W) :-
752 add_internal_error('Waitflags in wrong format',ground_constraintprop_wait_flags(W)).
753
754
755
756 :- use_module(clpfd_interface,[clpfd_in_domain/1, clpfd_labeling/2]).
757 :- use_module(library(lists),[reverse/2]).
758
759 % deprecated:
760 ground_wf(Prio,WF) :-
761 (var(WF) -> WF=Prio ; if(WF=Prio,true,add_internal_error('Illegal waitflag: ',ground_wf(Prio,WF)))).
762
763 :- if(environ(debug_waitflags,true)).
764 % provide feedback about waitflag grounding
765 init_wf_infos_for_grounding(Infos,NewInfos) :-
766 %print(start_wf_grounding(Infos)),nl,
767 ord_add_element(Infos,wf_indent(_),NewInfos).
768
769 % provided indentation to see scope of grounding
770 indent_wf(WFInfos) :- member(wf_indent(Var),WFInfos),!,indent_var(Var).
771 indent_wf(_).
772 % indent based upon a shared variable and increment it
773 indent_var(X) :- var(X),!, X=s(_).
774 indent_var(s(X)) :- !,print('.'), indent_var(X).
775 indent_var(_) :- print('***').
776
777 :- use_module(library(terms),[term_hash/3]).
778 ground_prio(Prio,WFMin,Info,WFInfos) :- WFInfos \= '$debug_done',
779 (member(IM,WFInfos),get_info_context_description(IM,Msg) -> true ; Msg=''),
780 nl,indent_wf(WFInfos),
781 term_hash(Info,[if_var(ignore)],Hash),
782 format(' -> ~w --> #~w# ~w :: ~w ~w~n',[Prio,Hash,Info,WFMin,Msg]),
783 !,
784 ground_prio(Prio,WFMin,Info,'$debug_done'). % recursive, but this time normal clauses will apply
785 :- else.
786 init_wf_infos_for_grounding(I,I).
787 :- endif.
788 ground_prio(Prio,WF,Info,WFInfos) :- %print(ground_prio(Prio,WF,Info)),nl,
789 nonvar(WF),!,
790 (WF=[H|T] -> % we used sometimes to have a list of waitflags; ground one-by one;
791 add_internal_error('Deprecated grounding of lists:',ground_prio(Prio,WF,Info,WFInfos)),
792 reverse([H|T],Ls),
793 maplist(ground_wf(Prio),Ls)
794 ; ground(WF) -> true % already grounded by somebody else
795 ; add_internal_error('Waitflag already partially grounded: ',ground_prio(Prio,WF,Info))).
796 %ground_prio(Prio,WF,Info,WFInfos) :- get_debug_kernel_waitflags(Cntr),!,
797 % print(ground_prio(Cntr,Prio,WF,Info)),nl, WF=Prio, print(grounded_prio(Cntr,Prio,WF,Info)),nl.
798 ground_prio(Prio,WF,_Info,_WFInfos) :-
799 %external_functions:indent_format(user_output,'~w (~w)~n',[Prio,Info],no_trace),portray_attached_goals(WF),nl,
800 %% (debug:debug_mode(on) -> external_functions:indent_format(user_output,'~w (~w)~n',[Prio,Info],no_trace) ; true),
801 % print(ground(Prio,WF,Info)),nl, translate:print_frozen_info(WF),nl,
802 WF = Prio. % ground waitflag
803 % print(grounded(Prio,Info)),nl,nl, %%
804
805 %:- use_module(kernel_objects,[enum_warning_large/3]).
806 label_clpfd_variable(Variable) :- label_clpfd_variable(2097153,Variable).
807 label_clpfd_variable(Prio,Variable) :-
808 %print(label_clpfd_variable(Prio,Variable)),nl,
809 (gt_limit(Prio,2097152), % note Prio > test also works with float(X) terms !
810 check_if_labeling_domain_large(Variable,2097152,Size,RANGE) % {x|x:1..2**23 & x mod 2 = x mod 1001} takes about 2 minutes; {x|x:1..2**21 & x mod 2 = x mod 1001} takes about 30 seconds; 2**21 = 2097152, 2**23 = 8388608
811 -> %kernel_objects:enum_warning_large(Variable,'INTEGER VARIABLE',RANGE), % could in principle also be enumerated set; but extremely unlikely given size; enum_warning will generate virtual time-outs, ... -> we no longer do this; for AMASS scheduler.mch there is a time-out after step 301 otherwise
812 performance_messages:perfmessage(enumerating_large_integer_variable(RANGE)),
813 (number(Size)
814 -> clpfd_labeling([Variable],[bisect]) % ffc no longer required: just a single variable
815 ; add_warning(label_clpfd_variable,'Trying to enumerate FD Variable with infinite domain (possibly due to WD error) ',RANGE)
816 % or due to clpfd_tables element/3 adding unbounded variables for labeling (add_fd_variables_for_labeling)
817 )
818 % we use bisecting rather than default enumeration; e.g., x:1..8388610 & y:1..8388610 & x*x*x = y*y + y + 10 is relatively quickly determined to be unsatisfiable (380 ms)
819 % Note: test 1099 tests this
820 % TO DO: investigate whether it makes sense to use bisect for smaller values already
821 ? ; preferences:preference(randomise_enumeration_order,true) -> clpfd_interface:clpfd_randomised_in_domain(Variable)
822 ? ; clpfd_in_domain(Variable)). % TO DO: use random ?
823
824 :- use_module(clpfd_interface,[ clpfd_size/2]).
825 check_if_labeling_domain_large(H,Limit,Size,Res) :-
826 clpfd_size(H,Size),
827 gt_limit(Size,Limit),
828 clpfd_interface:clpfd_domain(H,From,To),
829 Res=From:To.
830
831 %check_if_labeling_domain_finite(H) :-
832 % clpfd_size(H,Size), number(Size).
833
834 % mix ProB labeling with CLP(FD) labeling
835 ground_waitflags_store_clpfd(Store,WFInfos) :-
836 my_get_wf_store_fdvars_atts(Store,Heap,FD),!,
837 ? ground_waitflags_store_clpfd_aux(Store,Heap,FD,WFInfos).
838 ground_waitflags_store_clpfd(Store,WFInfos) :-
839 add_internal_error('Illegal Waitflag: ',ground_waitflags_store_clpfd(Store,WFInfos)),fail.
840
841 ground_waitflags_store_clpfd_aux(Store,Heap,FDVars,WFInfos) :- FDVars \= [],
842 %my_get_fdvars_att(Store,FDVars),
843 %get_atts(Store,wf_fdvars(FDVars)), % there is a list of FD vars available
844 !,
845 ( avl_del_min_waitflag(Heap,Prio,wf(WFMin,Info),NewHeap)
846 -> % we have non-ground waitflags with a matching priority
847 (my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS),
848 fd_priority_leq_limit(NextFDVar,FDPrio,Prio)
849 ? -> enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS,WFInfos)
850 ; put_mutable_wf_store_attr(Store,NewHeap), % write the modified heap back
851 % debug:hit_counter(XXX),print(grounding(XXX,Prio,_Info)),nl, (XXX=16556->trace ; true),
852 ? ground_prio(Prio,WFMin,Info,WFInfos),
853 ? ground_waitflags_store_clpfd(Store,WFInfos) % continue recursively
854 )
855 ; % there are no pending waitflag
856 (my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS)
857 -> enum_fd_variable_store(Store,NextFDVar,RemainingFDVARS,WFInfos)
858 ; true % heap and FDVars list is empty, nothing more to do
859 )
860 ).
861 ground_waitflags_store_clpfd_aux(Store,Heap,[],WFInfos) :- % no FDVars are available to enumerate
862 avl_del_min_waitflag(Heap,Prio,wf(WFMin,Info),NewHeap),
863 !,
864 put_mutable_wf_store_attr(Store,NewHeap), % write the modified heap back
865 % debug:hit_counter(XXX),print(grounding(XXX,Prio,_Info)),nl, (XXX=16556->trace ; true),
866 ? ground_prio(Prio,WFMin,Info,WFInfos), % ground waitflag
867 ? ground_waitflags_store_clpfd(Store,WFInfos). % continue recursively
868 ground_waitflags_store_clpfd_aux(_Store,_Heap,[],_). % heap and FDVars list is empty, nothing more to do
869
870 %:- use_module(library(lists),[exclude/3]).
871 enum_fd_variable_store(Store,NextFDVar,RemainingFDVARS,WFInfos) :-
872 %exclude(nonvar,RemainingFDVARS,RemainingFDVARS2),
873 put_mutable_wf_fdvars_attr(Store,RemainingFDVARS),
874 %% print(labeling(NextFDVar,RemainingFDVARS)),nl, portray_fd_vars([NextFDVar|RemainingFDVARS]),nl,nl,
875 label_clpfd_variable(NextFDVar),
876 ground_waitflags_store_clpfd(Store,WFInfos).
877 % a version of the above where we know the priority
878 enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS,WFInfos) :-
879 %exclude(nonvar,RemainingFDVARS,RemainingFDVARS2), % does not seem to buy a lot
880 put_mutable_wf_fdvars_attr(Store,RemainingFDVARS),
881 %% print(labeling(NextFDVar,RemainingFDVARS)),nl, portray_fd_vars([NextFDVar|RemainingFDVARS]),nl,nl,
882 ? label_clpfd_variable(FDPrio,NextFDVar),
883 ? ground_waitflags_store_clpfd(Store,WFInfos).
884
885 :- if(false).
886 % small utility to trace through waitflag enumeration
887 :- dynamic spying_on/1.
888 spying_on(1).
889 spy_waitflags(_,_) :- \+ spying_on(_) , !.
890 spy_waitflags(Msg,Store) :- retract(spying_on(Nr)),print(Msg),nl,
891 (Nr=1
892 -> assertz(spying_on(Nr)),
893 print(' (j,t,p,q) ==> '),read_term(T,[]),
894 action(T,Store)
895 ; N1 is Nr-1, assertz(spying_on(N1))
896 ).
897 action(t,_Store) :- !,trace.
898 action(p,Store) :- !,portray_waitflags_store(Store), spy_waitflags('',Store).
899 action(q,_Store) :- !,retractall(spying_on).
900 action(Nr,_) :- number(Nr),!, retractall(spying_on(_)), assertz(spying_on(Nr)).
901 action(_,_).
902 :- endif.
903
904 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF), kernel_waitflags:portray_waitflags(WF),
905 kernel_waitflags:get_wait_flag(2,'Test',WF,WF2), when(nonvar(WF2),print(wf2)),
906 FD in 1..2,kernel_waitflags:add_fd_variable_for_labeling(FD,WF),
907 kernel_waitflags:portray_waitflags(WF), kernel_waitflags:ground_wait_flags(WF) )).
908 % PORTRAYING Waitflags
909 portray_waitflags(wfx(WF0,Store,WFE,Infos)) :- flush_output,
910 delete(Infos,call_stack(_),Infos2),
911 print('*WAITFLAG STORE: '),print(wfx(WF0,'STORE',WFE,Infos2)),nl,
912 portray_wait_flag_infos(Infos),nl,
913 (var(WF0) -> print('> WF0 :: '), portray_attached_goals(WF0),nl ; true),
914 portray_waitflags_store(Store),!,
915 (var(WFE) -> print('> WFE :: '), portray_attached_goals(WFE),nl ; true),
916 print('*END WAITFLAG STORE'),nl.
917 %my_portray_avl(Heap),nl.
918 portray_waitflags(none) :- !, print('*WAITFLAG STORE: none'),nl.
919 portray_waitflags(no_wf_available) :- !, print('*WAITFLAG STORE: none'),nl.
920 portray_waitflags(Store) :-
921 add_internal_error('Illegal Waitflag: ',portray_waitflags(Store)),flush_output.
922
923 portray_fd_vars([]) :- !, '$NOT_COVERED'('This is debugging code!').
924 portray_fd_vars([V|T]) :- print('> '), print(V), nonvar(V),!, nl,
925 portray_fd_vars(T), '$NOT_COVERED'('This is debugging code!').
926 portray_fd_vars([V|T]) :- clpfd_size(V,Size), !, print(' : '), print(Size),
927 portray_attached_goals(V),
928 portray_fd_vars(T),
929 '$NOT_COVERED'('This is debugging code!').
930 portray_fd_vars(X) :- print('> *** UNKNOWN FDVARS: '), print(X), nl,
931 '$NOT_COVERED'('This is debugging code!').
932
933 portray_waitflags_store(Store) :-
934 my_get_fdvars_att(Store,FDList),
935 (FDList=[] -> true ; print('FDVARS:'),nl,portray_fd_vars(FDList),nl),
936 my_get_wf_store_att(Store,Heap),
937 avl_to_list(Heap,List), portray_avl_els(List).
938
939 portray_avl_els([]).
940 portray_avl_els([Prio-wf(LWF,Info)|T]) :-
941 print('> '),print(Prio), print(' : '), print(LWF), print(' : '), portray_info(Info),nl,
942 portray_attached_goals(LWF),
943 portray_avl_els(T).
944
945 portray_info(label_clpfd_vars(Vars)) :- print('label_clpfd_vars :: '),
946 translate:l_print_frozen_info(Vars).
947 portray_info(I) :- print(I).
948
949 portray_attached_goals(V) :- V==[],!.
950 portray_attached_goals(LWF) :- nonvar(LWF),LWF=[H|T],!,
951 portray_attached_goals(H),
952 portray_attached_goals(T).
953 portray_attached_goals(LWF) :- frozen(LWF,Goal), print(' :: '), portray_goal(Goal),nl.
954
955 :- use_module(tools_printing,[print_term_summary/1]).
956 portray_goal((A,B)) :- !, portray_goal(A), print(','), print(' '), portray_goal(B).
957 portray_goal(A) :- print_term_summary(A).
958
959
960 :- public my_portray_avl/1.
961 my_portray_avl(V) :- var(V), !, add_internal_error('Variable: ',my_portray_avl(V)).
962 my_portray_avl(V) :- portray_avl(V).
963
964
965 quick_portray_waitflags(wfx(WF0,Store,WFE,_)) :-
966 my_get_wf_store_fdvars_atts(Store,Heap,FDList),
967 avl_domain(Heap,List),
968 format('% Waitflags-Store WF0=~w,WFE=~w, Flags=~w, FDVars=~w~n',[WF0,WFE,List,FDList]).
969 quick_portray_waitflags(no_wf_available) :-
970 format('% Waitflags-Store no_wf_available~n',[]).
971 quick_portray_waitflags_store(Store) :-
972 my_get_wf_store_fdvars_atts(Store,Heap,FDList),
973 avl_domain(Heap,List),
974 format('% Waitflags-Store Flages=~w, FDVars=~w~n',[List,FDList]).
975
976 % get information about the phase a Waitflag store is in
977 get_waitflags_phase(no_wf_available,Phase) :- Phase=no_wf_available.
978 get_waitflags_phase(wfx(WF0,_,WFE,_),Phase) :-
979 (var(WF0) -> Phase=phase0
980 ; \+ ground(WF0) -> Phase=phase1
981 ; ground(WFE) -> Phase=enumeration_finished
982 ; Phase = grounding). % TODO: get avl_min
983
984 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF),
985 kernel_waitflags:get_wait_flag1(WF,WF1), when(ground(WF1),X=a),
986 kernel_waitflags:ground_wait_flags(WF), X==a )).
987
988 % create a copy of the waitflags with WF0 and WFE as variable; should be finished with copy_wf_finish
989 % can be used if you temporarily want to disable non-deterministic enumeration, ...
990 % This is typically used within e.g., a local scope where a conjunction of predicates is added
991 % we start 1) by doing copy_wf_start(OuterWF,LocalWF), 2) assert all predicates,
992 % and then 3) call copy_wf_finish at the end of the block
993 % This ensures that during assertion of the predicates only efficient deterministic computations occur
994 copy_wf_start(wfx(_,WFStore,_,Infos),Info,wfx(_,WFStore,_,NewInfos)) :-
995 add_debug_wait_flag_info(Infos,wf_copy(Info),NewInfos).
996 copy_wf_start(no_wf_available,_,no_wf_available).
997
998 % just copy WF0 and WFE over to new waitflag
999 copy_wf_finish(wfx(WF0,_,WFE,_),wfx(WF0,Store,WFE2,Info)) :-
1000 ? (var(WFE) -> WFE2 = WFE
1001 ; % the outer WF is already fully grounded and will not be driven anymore
1002 % so now we ground all the constraints that have been set-up since copy_wf_start
1003 ground_wait_flags(wfx(WF0,Store,WFE2,Info))
1004 ).
1005 copy_wf_finish(no_wf_available,no_wf_available).
1006
1007
1008 /* create a copy where the enumeration_finished waitflag is shared;
1009 the inner enumeration_finished wait flags should normally not be grounded */
1010 create_inner_wait_flags(wfx(_,OuterStore,WFE,Infos),Info,wfx(_WF0Inner,S,WFEInner,NewInfos)) :-
1011 add_debug_wait_flag_info(Infos,Info,NewInfos),
1012 copy_wfe_to_inner(WFE,WFEInner),
1013 my_get_all_wf_info_atts(OuterStore,DynInfos), % copy dynamic wf_infos from outer to inner store
1014 init_wait_flags_store_with_dynamic_infos(S,DynInfos).
1015
1016 create_inner_wait_flags(no_wf_available,_,no_wf_available).
1017
1018 % was using: get_inner_enumeration_over_wait_flag/get_enumeration_almost_finished_wait_flag
1019 %:- block copy_wfe_to_inner(-,?,?).
1020 %copy_wfe_to_inner(_,WFE,WFE).
1021
1022 :- block copy_wfe_to_inner(-,-).
1023 copy_wfe_to_inner(WFE,_) :- var(WFE),!. % inner was grounded before outer
1024 copy_wfe_to_inner(WFE,WFE).
1025 % copy enumeration finished waitflag from inner computation if WFE was not grounded there
1026 % assumes inner WF enumeration is finished
1027 copy_wfe_from_inner_if_necessary(wfx(_,_,WFEInner,_),wfx(_,_,WFE,_)) :- var(WFEInner),!, WFE=WFEInner.
1028 copy_wfe_from_inner_if_necessary(_,_).
1029
1030 % is very similar to ground_constraintprop_wait_flags, but will also ground EF if no abort is pending
1031 ground_inner_wait_flags(WF) :-
1032 ? ground_constraintprop_wait_flags(WF),
1033 ground_ef_wait_flag_unless_abort_pending(WF).
1034
1035 % copy WF0 and WFE, set up a new store for the rest
1036 copy_wf01e_wait_flags(wfx(WF0,WFX,WFE,Infos),wfx(WF0,S,WFE,Infos)) :-
1037 init_wait_flags_store(S),
1038 get_waitflag_from_store(1,copy_wf01e,WFX,WF1), % now ground wait flag 1 if it is grounded in the orginal WF store
1039 copy_wf01e_wait_flags_aux(WF1,S).
1040 copy_wf01e_wait_flags(no_wf_available,no_wf_available).
1041
1042 :- block copy_wf01e_wait_flags_aux(-,?).
1043 copy_wf01e_wait_flags_aux(_,S) :-
1044 ground_waitflags_store_up_to_no_clpfd(1,S,[]). % no CLPFD - labeling required until prio 1
1045
1046 %--------------
1047
1048 % the following is useful when new predicates are checked during enumeration
1049 % creates a copy of the wait_flags, except for WF0; this needs to be grounded separately with grd_wf0/1 or clone_finish
1050 % thanks to the copy, WF0 is a variable again and we give priority to propagations that create avl_sets
1051 clone_wait_flags_from1(wfx(_WF0,S,WFE,Infos),Ground,wfx(_NewWF0,S2,NewWFE,Infos)) :-
1052 (nonvar(WFE)
1053 -> Ground=ground_upon_finish,
1054 add_debug_message(clone_wait_flags_from1,'Cloning WF store whose enumeration is finished: ',Infos)
1055 ; Ground=no_grounding,
1056 NewWFE=WFE % waitflag store still being driven by enumerator, no need to ground/enumerate afterwards
1057 ),
1058 deref_store(S,S2).
1059 clone_wait_flags_from1(no_wf_available,no_grounding,no_wf_available).
1060
1061 % call after clone_wait_flags_from1, after you have set up all predicates/constraints
1062 clone_wait_flags_from1_finish(wfx(WF0,_,_,_),Ground,wfx(WF0,S2,NewWFE,Infos)) :- % finish copying by copying over WF0
1063 ? (Ground=no_grounding -> true
1064 ; ground_wait_flags(wfx(WF0,S2,NewWFE,Infos)) % the cloned store is not being enumerated anymore; do it ourselves
1065 ).
1066 clone_wait_flags_from1_finish(no_wf_available,_,no_wf_available).
1067
1068 %--------------
1069
1070 get_wait_flag0(wfx(WF0,_,_,_),WF0).
1071 get_wait_flag0(no_wf_available,0).
1072
1073 %get_wait_flag0(WFX,WF0) :- get_wait_flag(0,WFX,WF0). /* when this is ground you can do det. propagatons */
1074 get_wait_flag1(WFX,WF1) :- get_wait_flag(1.0,WFX,WF1). /* when this is ground you can do det. propagatons */
1075 get_wait_flag1(Info,WFX,WF1) :- %print(getwf1(Info)),nl, portray_waitflags(WFX),nl,
1076 get_wait_flag(1.0,Info,WFX,WF1).
1077
1078 get_binary_choice_wait_flag(_,no_wf_available,WF2) :- !, WF2=2.
1079 get_binary_choice_wait_flag(Info,WF,WF2) :- get_preference(data_validation_mode,true),!,
1080 get_binary_choice_wait_flag_exp_backoff(1048576,Info,WF,WF2).
1081 % in DV mode we do not want to enumerate bool(.) or similar; we want to wait for data enumeration to decide those
1082 % prio was 1048576, caused slowdown for TYPES_AUTORISES_RVF3_GEN__MRGA.mch before improvement to b_check_exists_wfwd
1083 get_binary_choice_wait_flag(Info,WFX,WF2) :- !, get_binary_choice_wait_flag_exp_backoff(4,Info,WFX,WF2).
1084 %get_binary_choice_wait_flag(Info,WFX,WF2) :- % use 3 rather than 2 to give priority over enumeration choicepoints
1085 % get_wait_flag(4,Info,WFX,WF2). /* when this is ground you can do binary propagatons */
1086
1087 %get_middle_wait_flag(Info,WFX,WFn) :- get_wait_flag(10,Info,WFX,WFn).
1088
1089 ?get_last_wait_flag(Info,WFX,Res) :- last_finite_priority(P), get_wait_flag(P,Info,WFX,Res).
1090
1091 % ------------------------
1092 get_wait_flag_infos(wfx(_,_,_,Infos),Infos).
1093 get_wait_flag_infos(no_wf_available,[]).
1094
1095 portray_wait_flag_infos(wfx(_,_,_,Infos)) :- !, portray_wait_flag_infos(Infos).
1096 portray_wait_flag_infos(no_wf_available) :- !.
1097 portray_wait_flag_infos(List) :- maplist(portray_wait_flag_info,List).
1098
1099 portray_wait_flag_info(call_stack(CallStack)) :- !, translate_call_stack(CallStack,TS), write(TS).
1100 portray_wait_flag_info(X) :- write(X),nl.
1101
1102
1103 :- use_module(library(ordsets),[ord_member/2,ord_add_element/3]).
1104 get_wait_flag_info(wfx(_,_,_,Infos),Info) :- member(Info,Infos).
1105
1106 % use if the Info field is known, requires ordered/sorted Infos list
1107 % used for wfx_no_enumeration information, see test 1368
1108 is_wait_flag_info(wfx(_,_,_,Infos),Info) :-
1109 (nonvar(Infos) -> ord_member(Info,Infos) ; print(var_waitflag),nl,fail).
1110
1111 %set_wait_flag_infos(wfx(WF0,Store,WFE,_),Infos,wfx(WF0,Store,WFE,Infos)).
1112 %set_wait_flag_infos(no_wf_available,_,no_wf_available).
1113
1114 % add new Info entry to WF store's info list
1115 add_wait_flag_info(wfx(WF0,Store,WFE,Infos),Info,wfx(WF0,Store,WFE,NewInfos)) :-
1116 (ord_add_element(Infos,Info,NewInfos) -> true %, print(added_wf_infos(NewInfos)),nl
1117 ; add_internal_error('Could not add_wait_flag_info:',(Info,Infos)),
1118 NewInfos = [Info|Infos]).
1119 add_wait_flag_info(no_wf_available,_,no_wf_available).
1120
1121
1122 % CALL STACK related predicates
1123 % --------------
1124 :- use_module(tools_lists,[ord_update_nonvar/4]).
1125 % push call stack information into the WF info field
1126 push_wait_flag_call_stack_info(no_wf_available,_,WF2) :- !, WF2=no_wf_available.
1127 push_wait_flag_call_stack_info(wfx(WF0,Store,WFE,Infos),CallInfo,wfx(WF0,Store,WFE,NewInfos)) :-
1128 ord_update_nonvar(call_stack(Stack),Infos,call_stack([CallInfo|Stack]),NewInfos),!,
1129 %print(push_call_stack(CallInfo,Stack)),nl,
1130 (var(Stack) -> Stack=[] % no call stack available yet
1131 ; true).
1132 push_wait_flag_call_stack_info(WF,CallInfo,WF) :- add_internal_error('Pushing call stack failed: ',CallInfo).
1133
1134 % only push in TRACE_INFO mode to avoid performance hit in regular mode
1135 opt_push_wait_flag_call_stack_info(WF,_,WF2) :- preference(provide_trace_information,false),!, WF2=WF.
1136 opt_push_wait_flag_call_stack_info(WF,CallInfo,WF2) :-
1137 push_wait_flag_call_stack_info(WF,CallInfo,WF2).
1138
1139 debug_opt_push_wait_flag_call_stack_info(WF,CallInfo,WF2) :-
1140 (debug_mode(off) -> WF2=WF
1141 ; opt_push_wait_flag_call_stack_info(WF,CallInfo,WF2)
1142 ).
1143
1144 :- use_module(tools_lists,[ord_member_nonvar_chk/2]).
1145 add_call_stack_to_span(Span,wfx(_,_,_,Infos),NewSpan) :-
1146 (var(Infos) -> write(illegal_wfx),nl,fail ; true),
1147 ord_member_nonvar_chk(call_stack(CallStack),Infos),
1148 !,
1149 (Span = pos_context(Span1,call_stack(OldStack),Span2), % we already have a call_stack
1150 merge_call_stack(OldStack,CallStack,NewStack)
1151 -> NewSpan = pos_context(Span1,call_stack(NewStack),Span2)
1152 ; % we do not already have merged in a call-stack
1153 NewSpan = pos_context(Span,call_stack(CallStack),unknown)
1154 ).
1155 add_call_stack_to_span(Span,_,Span).
1156
1157 merge_call_stack(OldStack,AddStack,NewStack) :-
1158 reverse(OldStack,OR), OR=[FirstOld|_],
1159 reverse(AddStack,AR), AR=[FirstAdd|_],
1160 (FirstOld \= FirstAdd % we use \= and not \== as entries can contain different Prolog variables
1161 -> append(OldStack,AddStack,NewStack) % we have not yet incorporated the outer stack
1162 ; append(AR,_,OR) -> NewStack = OldStack % old stack incorporates new one; this is often (always?) the case
1163 ; NewStack = AddStack).
1164
1165 % a variation of add_error/4
1166 add_error_wf(Src,Msg,V,Span,WF) :-
1167 add_call_stack_to_span(Span,WF,NewSpan),
1168 add_error(Src,Msg,V,NewSpan).
1169
1170 add_internal_error_wf(Src,Msg,V,Span,WF) :-
1171 add_call_stack_to_span(Span,WF,NewSpan),
1172 add_internal_error(Src,Msg,V,NewSpan).
1173
1174 % a variation of add_error/4
1175 add_warning_wf(Src,Msg,V,Span,WF) :-
1176 add_call_stack_to_span(Span,WF,NewSpan),
1177 add_warning(Src,Msg,V,NewSpan).
1178
1179 % a variation of add_message/4
1180 add_message_wf(Src,Msg,V,Span,WF) :-
1181 add_call_stack_to_span(Span,WF,NewSpan),
1182 add_message(Src,Msg,V,NewSpan).
1183
1184 :- use_module(translate,[translate_call_stack/2]).
1185 portray_call_stack(Var) :- var(Var),!,
1186 add_internal_error('Illegal var WF:',portray_call_stack(Var)).
1187 portray_call_stack(wfx(_,_,_,Infos)) :-
1188 ord_member_nonvar_chk(call_stack(CallStack),Infos),!,
1189 translate_call_stack(CallStack,TS),!,write(TS),nl.
1190 portray_call_stack(_) :- write(no_call_stack_available),nl.
1191
1192 % copy call stack info from one wait flag to another
1193 copy_wait_flag_call_stack_info(wfx(_,_,_,FromInfos),wfx(WF0,Store,WFE,Infos),wfx(WF0,Store,WFE,NewInfos)) :-
1194 ord_member_nonvar_chk(call_stack(CallStack),FromInfos),!,
1195 ord_add_element(Infos,call_stack(CallStack),NewInfos).
1196 % Note: we do not copy expect_explicit_value (but we use this in b_trace_test_components only)
1197 copy_wait_flag_call_stack_info(_,WF,WF).
1198
1199 % get call stack, empty list if none available
1200 get_call_stack(wfx(_,_,_,FromInfos),Stack) :-
1201 ord_member_nonvar_chk(call_stack(CallStack),FromInfos),!,
1202 Stack=CallStack.
1203 get_call_stack(_,[]).
1204
1205 :- use_module(library(lists),[select/3]).
1206 init_quantifier_wait_flag(OuterWF,_QK,Paras,ParaValues,Pos,WF) :-
1207 ? select(QK,Pos,Pos1),
1208 detect_quantifier_kind(QK,QuantKind),!, % see if we have position infos which indicate the origin of the comprehension_set / quantifier
1209 simplify_span(Pos1,SPos),
1210 init_wait_flags_and_push_call_stack(OuterWF,quantifier_call(QuantKind,Paras,ParaValues,SPos),WF).
1211 init_quantifier_wait_flag(OuterWF,QuantKind,Paras,ParaValues,Pos,WF) :-
1212 simplify_span(Pos,SPos),
1213 init_wait_flags_and_push_call_stack(OuterWF,quantifier_call(QuantKind,Paras,ParaValues,SPos),WF).
1214
1215 % check grounding of WFE flag occurs after everything else
1216 :- public observe_wait_flags_wfe/1. % debugging utility
1217 observe_wait_flags_wfe(wfx(WF0,WFX,WFE,Infos)) :- !, obs(WF0,WFX,WFE,Infos).
1218 observe_wait_flags_wfe(_).
1219 :- block obs(?,?,-,?).
1220 obs(WF0,WFX,WFE,Infos) :-
1221 add_message_wf(kernel_waitflags,'WFE grounded:',WFE,unknown,wfx(WF0,WFX,WFE,Infos)),
1222 var(WF0),!,
1223 add_error_wf(kernel_waitflags,'WF0 not grounded before WFE:',WF0,unknown,wfx(WF0,WFX,WFE,Infos)).
1224 obs(WF0,WFX,WFE,Infos) :- my_get_fdvars_att(WFX,FDList), FDList \= [], !,
1225 add_error_wf(kernel_waitflags,'FDVars not grounded before WFE:',FDList,unknown,wfx(WF0,WFX,WFE,Infos)).
1226 obs(WF0,WFX,WFE,Infos) :- my_get_wf_store_att(WFX,Heap), avl_to_list(Heap,List), List \= [], !,
1227 add_error_wf(kernel_waitflags,'Waitflags not grounded before WFE:',List,unknown,wfx(WF0,WFX,WFE,Infos)).
1228 obs(_WF0,_WFX,_WFE,_Infos).
1229
1230 % a version of opt_push_wait_flag_call_stack_info for quantifiers
1231 opt_push_wait_flag_call_stack_quantifier_info(WF,_,_,_,_,WF2) :-
1232 preference(provide_trace_information,false),!, WF2=WF.
1233 opt_push_wait_flag_call_stack_quantifier_info(no_wf_available,_,_,_,_,WF2) :- !, WF2=no_wf_available.
1234 opt_push_wait_flag_call_stack_quantifier_info(WF,QuantKind,Paras,ParaValues,Pos,WF2) :-
1235 simplify_span(Pos,SPos),
1236 opt_push_wait_flag_call_stack_info(WF,quantifier_call(QuantKind,Paras,ParaValues,SPos),WF2).
1237
1238 detect_quantifier_kind(prob_annotation('LAMBDA'),lambda).
1239 detect_quantifier_kind(quantifier_kind(QK),QK).
1240
1241 %init_wait_flags_and_push_call_stack(_,_,WF) :- !, init_wait_flags(WF). % comment in to disable tracking quantifiers
1242 % makes a difference for memoize test 1968 with recursive calls
1243 init_wait_flags_and_push_call_stack(OuterWF,CallStackTerm,WF) :-
1244 init_wait_flags_and_push_call_stack(OuterWF,CallStackTerm,[],WF).
1245 init_wait_flags_and_push_call_stack(OuterWF,CallStackTerm,OtherWFInfos,WF) :-
1246 get_call_stack(OuterWF,OuterCallStack),
1247 %print(init_quantifier(QuantKind,Paras,OuterCallStack)),nl,
1248 CallStack = [CallStackTerm|OuterCallStack],
1249 get_wf_all_dynamic_infos(OuterWF,DynInfos),
1250 copy_new_important_wf_infos(OuterWF,OtherWFInfos,NewWFInfos),
1251 init_wait_flags_with_sd_infos(WF,[call_stack(CallStack)|NewWFInfos],DynInfos).
1252
1253 init_wait_flags_with_call_stack(WF,CallStack) :-
1254 init_wait_flags_with_infos(WF,[call_stack(CallStack)]).
1255
1256 % the following is optimised for the fact that expect_explicit_value is the only important info
1257 % other than call_stack which is dealt with separately:
1258 copy_new_important_wf_infos(OuterWF,OtherWFInfos,NewWFInfos) :-
1259 (get_wait_flag_infos(OuterWF,OuterWFInfos),
1260 member(expect_explicit_value,OuterWFInfos),
1261 nonmember(expect_explicit_value,OtherWFInfos)
1262 -> NewWFInfos = [expect_explicit_value|OtherWFInfos]
1263 ; NewWFInfos = OtherWFInfos).
1264
1265 % :- use_module(library(lists),[include/3]).
1266 % get important static WF infos other than call-stack
1267 % call stack dealt with separately
1268 %get_important_wf_infos(WF,ImportantInfos) :-
1269 % get_wait_flag_infos(WF,Infos),
1270 % include(is_important,Infos,ImportantInfos).
1271 %is_important(expect_explicit_value).
1272
1273 % --------------
1274
1275 :- if((environ(prob_safe_mode,true) ; environ(prob_src_profile,true) ; environ(prob_profile,true))).
1276 % TODO: should this be controlled also by prob_profiling_on preference
1277 add_debug_wait_flag_info(Infos,Info,NewInfos) :- ord_add_element(Infos,Info,NewInfos).
1278 init_debug_wait_flag_info([],Infos) :- !, Infos=[].
1279 init_debug_wait_flag_info([H|T],Infos) :- !, Infos=[H|T].
1280 init_debug_wait_flag_info(Info,[Info]).
1281 :- else.
1282 add_debug_wait_flag_info(Infos,_,Infos).
1283 init_debug_wait_flag_info(_,[]).
1284 :- endif.
1285 % ------------------------
1286
1287 % BINARY CHOICE WAITFLAGS
1288
1289 :- use_module(tools_platform, [max_tagged_integer/1, max_tagged_pow2/1]).
1290 % last power of 2 exponent that is still a finite priority
1291 last_finite_pow2_prio_exponent(Lim) :- max_tagged_pow2(Pow),
1292 Lim is Pow-1. % stay below last_finite_priority, i.e., max_tagged_integer-1024
1293
1294 % utility to get power of two priority:
1295 get_pow2_binary_choice_priority(Exp,Prio) :- last_finite_pow2_prio_exponent(Lim),
1296 (Exp =< Lim -> Prio is floor(2**Exp)
1297 ; Prio is floor(2**Lim)).
1298
1299 % get a binary choice wait flag, start with 2 but for every further call double the priority (exponential backoff)
1300 % idea: give priority to data enumerations when too many choice points arise
1301 % TO DO: when triggering: keep the WF info with an empty trigger or store in separate info field
1302 get_binary_choice_wait_flag_exp_backoff(Info,WFX,WF2) :-
1303 get_binary_choice_wait_flag_exp_backoff(2,Info,WFX,WF2).
1304 get_binary_choice_wait_flag_exp_backoff(_,_Info,no_wf_available,WF2) :- !, WF2=2.
1305
1306 % StartPrio should normally be a power of 2:
1307 get_binary_choice_wait_flag_exp_backoff(MinPrio,Info,wfx(WF0,Store,WFE,_II),WF2) :-
1308 (nonvar(WFE) -> add_message(kernel_waitflags,'Getting waitflag from grounded store: ',Info),
1309 % probably a good idea to use clone_wait_flags_from1 if this happens
1310 WF2=WF0 % use at least WF0 for WF2, maybe it is still being enumerated
1311 ; my_get_wf_store_att(Store,Heap),
1312 large_finite_priority(Lim), % avoid creating too big numbers and stay in finite area
1313 (MinPrio < 1 -> StartPrio=1 ; StartPrio=MinPrio),
1314 get_bin_aux(StartPrio,Lim,Info,Heap,NewHeap,WF2),
1315 put_mutable_wf_store_attr(Store,NewHeap)
1316 ).
1317
1318 % to do : maybe store attribute of current exponential ??
1319 get_bin_aux(Prio,Lim,Info,Heap,NewHeap,WF2) :-
1320 avl_fetch(Prio,Heap,wf(WFs,OldInfo)),
1321 !,
1322 (%Prio >= Lim -> pop_waitflag(WFs,WF2,_), % should we get last waitflag or simply push
1323 % NewHeap=Heap ; /* for large finite priorities single waitflag stored */
1324 Prio<Lim, OldInfo = '$binary'(_)
1325 -> double_prio(Prio,P2), get_bin_aux(P2,Lim,Info,Heap,NewHeap,WF2)
1326 ; push_waitflag(WFs,WF2,WFs2),
1327 avl_store(Prio,Heap,wf(WFs2,'$binary'(Info)),NewHeap) % just update Info; so that next time we double
1328 ).
1329 get_bin_aux(Prio,_,Info,Heap,NewHeap,WF2) :- % we have found an unused power of 2
1330 avl_store(Prio,Heap,wf(WF2,'$binary'(Info)),NewHeap).
1331
1332 % ------------------------
1333
1334 get_enumeration_finished_wait_flag(wfx(_,_,WFE,_),Res) :- !,Res=WFE.
1335 get_enumeration_finished_wait_flag(no_wf_available,WFE) :- !,WFE=1.
1336 get_enumeration_finished_wait_flag(W,E) :-
1337 add_internal_error('Waitflags in wrong format: ',get_enumeration_finished_wait_flag(W,E)).
1338
1339
1340 ground_det_wait_flag(wfx(WF0,Store,_,WFInfos)) :- !, %% print(ground_det_wait_flag),nl,
1341 ? grd_wf0(WF0),
1342 deref_store(Store,DStore),
1343 init_wf_infos_for_grounding(WFInfos,WFInfos2),
1344 ground_waitflags_store_up_to_no_clpfd(1,DStore,WFInfos2). % no CLPFD labeling required for prio until 1
1345 ground_det_wait_flag(no_wf_available) :- !.
1346 ground_det_wait_flag(W) :-
1347 add_internal_error('Waitflags in wrong format: ',ground_det_wait_flag(W)).
1348
1349 ground_wait_flag_to(wfx(WF0,Store,_,WFInfos),Limit) :- !,%% print(ground_det_wait_flag),nl,
1350 grd_wf0(WF0),
1351 deref_store(Store,DStore),
1352 init_wf_infos_for_grounding(WFInfos,WFInfos2),
1353 ? ground_waitflags_store_clpfd_up_to(Limit,DStore,WFInfos2).
1354 ground_wait_flag_to(no_wf_available,_) :- !.
1355 ground_wait_flag_to(W,Limit) :-
1356 add_internal_error('Waitflags in wrong format: ',ground_wait_flag_to(W,Limit)).
1357
1358 useless_wait_flag(treat_next_integer(_VarID,Val,Rest)) :- Rest==[],
1359 kernel_tools:ground_value(Val).
1360 useless_wait_flag(tighter_enum(_VarID,Val,_Type)) :- %ground(Val).
1361 kernel_tools:ground_value(Val).
1362 %nonvar(Val), quick_ground(Val).
1363 %quick_ground(int(N)) :- integer(N).
1364 %quick_ground(string(S)) :- atom(S).
1365
1366 % show which waitflags are actually useless
1367 % TODO: think about pruning the WF store; e.g., when calling deref_store ??
1368 :- public portray_useless_waitflags_in_store/1.
1369 portray_useless_waitflags_in_store(Store) :-
1370 my_get_wf_store_att(Store,Heap),
1371 avl_member(AMin,Heap,wf(_,Info)),
1372 useless_wait_flag(Info),
1373 format(' useless waitflag ~w --> ~w~n',[AMin,Info]),
1374 fail.
1375 portray_useless_waitflags_in_store(_).
1376
1377
1378
1379 ground_waitflags_store_up_to_no_clpfd(Prio,Store,WFInfos) :- % print(prio(Prio)),nl,
1380 my_get_wf_store_att(Store,Heap), % portray_waitflags_store(Store), %
1381 ( avl_min(Heap,Min,_) -> % heap is non-empty, first element is WF with priority Min
1382 ( Min =< Prio -> % we have non-ground waitflags with a matching priority
1383 (avl_del_min_waitflag(Heap,AMin,wf(WFMin,Info),NewHeap),AMin==Min % remove the first element
1384 -> true
1385 ; add_internal_error('Could not remove min. in ground_waitflags_store_up_to_no_clpfd: ',Min:Info:Heap), fail
1386 ),
1387 put_mutable_wf_store_attr(Store,NewHeap), % write the modified heap back
1388 %% print(grounding2(Prio,_Info)),nl, %%
1389 ground_prio(Min,WFMin,Info,WFInfos),
1390 ground_waitflags_store_up_to_no_clpfd(Prio,Store,WFInfos) % continue recursively
1391 ; % no more matching waitflags present
1392 true) % stop here
1393 ; % heap is empty, nothing more to do
1394 true). % stop here
1395
1396 ground_waitflags_store_clpfd_up_to(Limit,Store,WFInfos) :-
1397 my_get_wf_store_fdvars_atts(Store,Heap,FDVars),
1398 !,
1399 ( avl_del_min_waitflag(Heap,Prio,wf(WFMin,Info),NewHeap),
1400 Prio =< Limit
1401 -> % we have non-ground waitflags with a matching priority
1402 ( my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS),
1403 fd_priority_leq_limit(NextFDVar,FDPrio,Limit)
1404 -> (FDPrio > Limit -> true
1405 ? ; enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS,WFInfos))
1406 ; put_mutable_wf_store_attr(Store,NewHeap), % write the modified heap back
1407 ? ground_prio(Prio,WFMin,Info,WFInfos), % ground waitflag
1408 ? ground_waitflags_store_clpfd_up_to(Limit,Store,WFInfos) % continue recursively
1409 )
1410 ; % no pending waitflag
1411 my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS),
1412 fd_priority_leq_limit(NextFDVar,FDPrio,Limit)
1413 -> enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS,WFInfos)
1414 ; true % heap and FDVars list is empty, nothing more to do
1415 ).
1416 ground_waitflags_store_clpfd_up_to(Limit,Store,WFInfos) :-
1417 add_internal_error('Illegal Waitflag: ',ground_waitflags_store_clpfd_up_to(Limit,Store,WFInfos)),fail.
1418
1419 % --------------------
1420
1421 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF),
1422 kernel_waitflags:get_wait_flag(10001,test1,WF,WF1),
1423 kernel_waitflags:get_wait_flag(10001,test2,WF,WF2),
1424 check_eq(WF,wfx(_,Store,_,_)),
1425 kernel_waitflags:my_get_wf_store_fdvars_atts(Store,Heap,_FDVars),
1426 kernel_waitflags:avl_del_min_waitflag(Heap,Prio1,wf(WFMin1,_Info1),Heap2),
1427 check_eqeq(Prio1,10001), check_eqeq(WFMin1,WF1),
1428 kernel_waitflags:avl_del_min_waitflag(Heap2,Prio2,wf(WFMin2,_Info2),_),
1429 check_eqeq(Prio2,10001), check_eqeq(WFMin2,WF2)
1430 )).
1431
1432 % a conditional version of avl_del_min: where we either delete or update the minimal element
1433
1434 :- if(current_prolog_flag(dialect, swi)).
1435 avl_del_min_waitflag(AVL0, Key, wf(NextWF,Info), AVL) :-
1436 avl_del_min(AVL0,Key,Val0,AVL1),
1437 Val0 = wf(WFMin,Info),
1438 pop_waitflag(WFMin,NextWF,RemainingWFMin),
1439 (RemainingWFMin==[] -> AVL=AVL1
1440 ; avl_store(Key,AVL1,wf(RemainingWFMin,Info),AVL)
1441 ).
1442
1443 :- else.
1444 % optimized code to delete and update in one traversal; requires access to inner predicates of library(avl)
1445
1446 avl_del_min_waitflag(AVL0, Key, Val, AVL) :-
1447 avl_del_min_wf_aux(AVL0, Key, Val, AVL, _).
1448
1449 avl_del_min_wf_aux(node(K,V,B,L,R), Key, Val, AVL, Delta) :-
1450 ( L == empty ->
1451 Key = K,
1452 V = wf(WFMin,Info),
1453 pop_waitflag(WFMin,NextWF,RemainingWFMin),
1454 Val = wf(NextWF,Info),
1455 (RemainingWFMin==[] -> % single waitflag
1456 %print(single_wf(Key,WFMin,Info)),nl,
1457 AVL = R, Delta=1 % we delete the minimal element
1458 ; % there are multiple waitflags for the same priority
1459 % print(multiple_wf(Key,WFMin,Info)),nl,
1460 AVL = node(K,wf(RemainingWFMin,Info),B,L,R), % update AVL with remaining waitflags
1461 Delta = 0 % we do not delete the node, but update it
1462 )
1463 ; avl_del_min_wf_aux(L, Key, Val, L1, D1),
1464 B1 is B+D1,
1465 avl:avl(B1, K, V, L1, R, AVL),
1466 avl:avl_shrinkage(AVL, D1, Delta)
1467 ).
1468
1469 :- endif.
1470
1471 % -------------------
1472
1473 update_waitflag(Prio,CurrentWaitflag,NewWaitFlag,WF) :-
1474 /* if the CurrentWaitflag is already ground and now a new WF with lower priority has been added to the store:
1475 create a new non-ground waitflag to give priority to new WF with lower priority value */
1476 (var(CurrentWaitflag) -> NewWaitFlag=CurrentWaitflag
1477 ; WF=wfx(_WF0,Store,_,_),my_get_wf_store_att(Store,Heap),
1478 (avl_min(Heap,Min,wf(_NewerWF,_Info)),
1479 (is_inf_or_overflow_card(Prio) ; Min<Prio)
1480 -> get_waitflag_from_store(Prio,updated_wf,Store,NewWaitFlag) % could be optimized
1481 % ,print(updating_wf(Prio,CurrentWaitflag,NewWaitFlag,Min)),nl
1482 ; fdvar_with_higher_prio_exists(Store,Prio) -> get_waitflag_from_store(Prio,updated_wf,Store,NewWaitFlag)
1483 ; NewWaitFlag=CurrentWaitflag
1484 )
1485 ).
1486
1487 fdvar_with_higher_prio_exists(Store,Limit) :-
1488 my_get_fdvars_att(Store,FDVars),
1489 my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,_),
1490 fd_priority_leq_limit(NextFDVar,_,Limit).
1491
1492 % get the minimum waitflag, if it exists
1493 get_minimum_waitflag_prio(wfx(WF0,Store,_,_),MinPrio,Info) :-
1494 (var(WF0) -> MinPrio=0, Info=wf0
1495 ; WF0=s(WF00), var(WF00) -> MinPrio = 1, Info=wf0_nonvar
1496 ; my_get_wf_store_att(Store,Heap),
1497 avl_min(Heap,MinPrio,wf(_LWF,Info))).
1498
1499
1500 add_wd_error(Msg,Term,WF) :-
1501 try_extract_span(Term,Span), % try extract span if possible;
1502 % e.g., for {x|x>10 } = res & card(res)=10 we have a closure/3 as Term
1503 add_wd_error_span(Msg,Term,Span,WF).
1504
1505 try_extract_span(closure(_,_,B),Span) :- !, Span=B.
1506 try_extract_span(b(_,_,Infos),Span) :- !, Span=Infos.
1507 try_extract_span(_,unknown).
1508
1509 add_wd_error_set_result(Msg,Term,Result,ResultValue,Span,WF) :-
1510 is_wd_guarded_result(Result), % just dummy co-routine to detect variables which have WD assignments pending on them; should not be enumerated
1511 % add well-definedness error but also set Result Variable to ResultValue to trigger pending co-routines if wd_guarded
1512 add_abort_error7(well_definedness_error,Msg,Term,Result,ResultValue,Span,WF).
1513
1514 add_wd_error_span(Msg,Term,Span,WF) :-
1515 add_abort_error_span(well_definedness_error,Msg,Term,Span,WF).
1516 % the same but adding a WD error directly, without any delay:
1517 add_wd_error_span_now(Msg,Term,Span,WF) :-
1518 add_abort_error2(true,well_definedness_error,Msg,Term,0,0,Span,WF).
1519
1520 % mark a variable as being wd_guarded
1521 % TODO: use put_wf_abort_pending(X).
1522 :- block is_wd_guarded_result(-).
1523 is_wd_guarded_result(string(X)) :- !, is_wd_guarded_result(X).
1524 is_wd_guarded_result(int(X)) :- !, is_wd_guarded_result(X).
1525 is_wd_guarded_result(fd(X,_)) :- !, is_wd_guarded_result(X).
1526 is_wd_guarded_result(_).
1527
1528 add_abort_error_span(ErrType,Msg,Term,Span,WF) :- add_abort_error7(ErrType,Msg,Term,0,0,Span,WF).
1529
1530
1531 :- use_module(probsrc(debug), [debug_mode/1]).
1532 add_abort_error7(_ErrType,_Msg,_Term,_Result,_ResultValue,_Span,_WF) :-
1533 preference(disprover_mode,true),
1534 !, % When Disproving we can assume well-definedness of the PO; sometimes the relevant hypotheses will be filtered out
1535 fail.
1536 add_abort_error7(ErrType,Msg,Term,Result,ResultValue,Span,WF) :-
1537 preference(raise_abort_immediately,F), F \= false,
1538 !,
1539 (F=full -> AWF=1 % really raise immediately; may entail more spurious messages, particularly in WF0
1540 ; get_idle_wait_flag(add_abort_error7,WF,AWF), %get_wait_flag0(WF,AWF),
1541 (var(AWF),debug_mode(on) ->
1542 (pending_abort_error(WF)
1543 -> format(user_output,'Additional WD Error pending:~n',[]) % TODO: no use in calling add_abort_error2 below
1544 ; true),
1545 add_message(wd_error_pending,Msg,Term,Span)
1546 ; true),
1547 mark_pending_abort_error(WF,Msg,Term,Span)
1548 ),
1549 %when(nonvar(AWF),
1550
1551 %(tools_printing:print_error('Raising error immediately without checking rest of predicate for unsatisfiability (because RAISE_ABORT_IMMEDIATELY set to TRUE): error could be spurious!'),true)),
1552 string_concatenate('Raising error immediately without checking rest of predicate for unsatisfiability (because RAISE_ABORT_IMMEDIATELY set to TRUE or full): error could be spurious!\n! ',Msg,NewMsg),
1553 add_abort_error2(AWF,ErrType,NewMsg,Term,Result,ResultValue,Span,WF).
1554 add_abort_error7(ErrType,Msg,Term,Result,ResultValue,Span,WF) :-
1555 %add_message_wf(add_abort_error,'Possible WD Error: ',Term,Span,WF), % happens a lot in test 1886
1556 mark_pending_abort_error(WF),
1557 get_enumeration_finished_wait_flag(WF,AWF), % infinite enumeration will never result in failure ? and thus never discard a WD error ? --> better to activate WD/abort error earlier, but the following does not fully work:
1558 %integer_priority(P), get_wait_flag(P,add_abort_error7,WF,AWF), % causes problem for test 1122
1559 add_abort_error2(AWF,ErrType,Msg,Term,Result,ResultValue,Span,WF).
1560
1561
1562 :- use_module(error_manager,[add_new_event_in_error_scope/1]).
1563 :- use_module(state_space,
1564 [store_abort_error_for_context_state_if_possible/4]).
1565 :- block add_abort_error2(-,?,?,?,?,?,?,?).
1566 add_abort_error2(wd_guarded,_Err,_Msg,_Term,Result,ResultValue,_Span,_WF) :- !,% ignoring error message; no-wd problem
1567 % set Result to default ResultValue; useful to get rid of pending co-routines; result will not be used
1568 (Result=ResultValue -> true ; print(add_abort_error_failure(Result,ResultValue)),nl).
1569 add_abort_error2(_AWF,ErrType,Msg,Term,_,_,Span,WF) :-
1570 add_call_stack_to_span(Span,WF,Span2),
1571 (register_abort_errors_in_error_scope(Level)
1572 -> (get_preference(provide_trace_information,false)
1573 -> format('Registering WD Error in error scope (~w): ~w~n',[Level,Msg])
1574 ; add_message_wf(wd_error,'Registering WD Error in error scope: ',Msg,Span,WF)
1575 ),
1576 assert(pending_inner_abort_error(Level,ErrType,Msg,Term,Span2))
1577 % ,trace,write(level(Level,ErrType)),nl,get_enumeration_finished_wait_flag(WF,WFE), when(nonvar(WFE),(write(abewf(Level,Msg)),nl,trace))
1578 % add_new_event_in_error_scope(abort_error(ErrType)) % do not register as error yet; we will copy it to the outer WF
1579 ; store_abort_error_for_context_state_if_possible(ErrType,Msg,Term,Span2)
1580 -> true
1581 ; add_error(add_abort_error2,'Could not store error: ',(Msg,Term),Span2)),
1582 % TODO: think about: throw(enumeration_warning('WD ERROR',ErrType,'','',throwing)), % TODO: throw and recognise as special error and treat e.g., in Keeping comprehension-set symbolic messages
1583 fail.
1584
1585 :- dynamic register_abort_errors_in_error_scope/0, pending_inner_abort_error/5.
1586
1587
1588 :- use_module(eventhandling,[register_event_listener/3]).
1589 :- register_event_listener(clear_specification,reset_waitflags, 'Reset kernel_waitflags.').
1590
1591 reset_waitflags :-
1592 retractall(register_abort_errors_in_error_scope),
1593 retractall(pending_inner_abort_error(_,_,_,_,_)),
1594 bb_put(wf_findall_nesting_level,0).
1595
1596 % clear pending inner abort errors
1597 % there is still a potential issue if a time-out appears just in the middle
1598 start_attach_inner_abort_errors(NewLevel,_PP) :-
1599 (bb_get(wf_findall_nesting_level,L) -> Level=L ; Level=0),
1600 NewLevel is Level+1,
1601 bb_put(wf_findall_nesting_level,NewLevel),
1602 %write(enter(NewLevel,PP)),nl,
1603 retractall(pending_inner_abort_error(NewLevel,_,_,_,_)).
1604
1605 % succeeds when abort errors should be asserted for later copying (at the end of a findall) to the outer WF
1606 register_abort_errors_in_error_scope(Level) :-
1607 register_abort_errors_in_error_scope,
1608 get_wf_findall_nesting_level(Level).
1609
1610 get_wf_findall_nesting_level(Level) :-
1611 (bb_get(wf_findall_nesting_level,L) -> Level=L
1612 ; write(missing_wf_findall_nesting_level),nl, Level=0).
1613
1614 % extract all pending inner abort errors and attach them to the outer WF store
1615 % should be called after ground_inner_wait_flags_in_context
1616 re_attach_pending_inner_abort_errors(Level,OuterWF,SomePending) :-
1617 get_wf_findall_nesting_level(L),
1618 %write(exit(L)),nl,
1619 (Level=L -> true
1620 ; write(unexpected_wf_findall_nesting(L,expected(Level))),nl
1621 % could happen if a time-out occured in middle of updating wf_findall_nesting_level info
1622 ),
1623 LL1 is Level-1, bb_put(wf_findall_nesting_level,LL1),
1624 findall(pending(ErrType,Msg,Term,Span),
1625 retract(pending_inner_abort_error(Level,ErrType,Msg,Term,Span)), PendingList),
1626 (PendingList=[]
1627 -> SomePending=none_pending
1628 ; SomePending=pending_abort_errors,
1629 % the next is useless when we call re_attach_pending_abort_errors in call_cleanup when the call failed
1630 maplist(re_add_pending_aux(OuterWF,LL1),PendingList)
1631 ).
1632
1633 re_add_pending_aux(WF,OuterLevel,pending(ErrType,Msg,Term,Span)) :-
1634 (get_preference(provide_trace_information,true), debug_mode(on)
1635 -> add_message_wf(wd_error,'Reattaching WD Error to outer WF: ',Msg,Span,WF)
1636 ; format('Reattaching error ~w in outer WF (~w)~n',[ErrType,OuterLevel])
1637 ),
1638 add_abort_error_span(ErrType,Msg,Term,Span,WF).
1639
1640 portray_pending :- pending_inner_abort_error(_Level,ErrType,Msg,Term,Span),
1641 add_message(ErrType,Msg,Term,Span),fail.
1642 portray_pending.
1643
1644 % add abort / state error directly (now), but do not fail unlike add_wd_error_span_now
1645 add_state_error_wf(ErrType,Msg,Term,Span,WF) :-
1646 add_call_stack_to_span(Span,WF,Span2),
1647 (store_abort_error_for_context_state_if_possible(ErrType,Msg,Term,Span2) -> true
1648 ; add_error_wf(ErrType,Msg,Term,Span2,WF)
1649 ).
1650
1651
1652 % ground enumeration finished waitflag unless abort is pending
1653 % useful inside of exists, ... to complete all pending co-routines
1654 ground_ef_wait_flag_unless_abort_pending(wfx(_WF0,_WFX,WFE,_)) :-
1655 var(WFE), %print(check_abort_pending(_WF0,_WFX,WFE)),nl,
1656 is_not_wf_abort_pending(WFE),
1657 !, %print(grd_ef(WFE)),nl,
1658 grd_ef(WFE).
1659 ground_ef_wait_flag_unless_abort_pending(_).
1660
1661 % special grounding of inner waitflags taking context (all_solutions, positive, negative) into account
1662 % one should call re_attach_pending_inner_abort_errors on the OuterWF after the forall/negation
1663 % in order to retract pending_inner_abort_error and re-attach the WD errors to the OuterWF
1664 ground_inner_wait_flags_in_context(all_solutions,WF) :-
1665 WF = wfx(_WF0,_WFX,WFE,_),!,
1666 ? ground_constraintprop_wait_flags(WF),
1667 (var(WFE),
1668 is_wf_abort_pending(WFE)
1669 -> !, % cut, we have found a WD error, whole findall result is useless anyway; cut
1670 % happens in tests 629, 1921, 1966, 2224
1671 debug_format(19,'WD Error(s) pending, could be spurious ~n',[]),
1672 assert(register_abort_errors_in_error_scope),
1673 call_cleanup(grd_ef(WFE), % will not raise WD errors but store them for OuterWF
1674 retract(register_abort_errors_in_error_scope))
1675 ; grd_ef(WFE)
1676 ).
1677 ground_inner_wait_flags_in_context(negative,WF) :- !,
1678 ? ground_inner_wait_flags_in_context(all_solutions,WF).
1679 ground_inner_wait_flags_in_context(_,WF) :-
1680 ground_wait_flags(WF). % TODO: do not ground WFE, ensure it is shared;
1681 % but currently positive context only used in external funs with wf_not_available
1682
1683
1684 % try and get a user-readable description of the context in which the WF store was created
1685 get_wait_flags_context_msg(wfx(_WF0,_WFX,_WFE,Infos),Msg) :-
1686 member(Info,Infos),
1687 get_info_context_description(Info,Msg).
1688
1689 :- use_module(bsyntaxtree, [get_texpr_ids/2]).
1690 get_info_context_description(call_stack(CS),Msg) :- translate_call_stack(CS,Msg).
1691 % TODO: do we need expansion_context anymore?
1692 get_info_context_description(expansion_context(Type,Parameters),Msg) :-
1693 (Parameters=[b(identifier(_),_,_)|_]
1694 -> get_texpr_ids(Parameters,Ids),
1695 get_msg_aux(Type,Ids,Parameters,Msg)
1696 ; get_msg_aux(Type,Parameters,[],Msg)
1697 ).
1698 get_info_context_description(expansion_context_with_pos(Type,Parameters,Info),Msg) :-
1699 get_msg_aux(Type,Parameters,Info,Msg).
1700 % also deal with : check_element_of_function_closure_nowf(MemoID)
1701
1702 get_msg_aux(Type,Parameters,Info,Msg) :-
1703 extract_span_description(Info,PosMsg),!, % we could do this only if preference trace_info set ?
1704 ajoin_with_sep(['expanding', Type, 'at', PosMsg, 'over ids'|Parameters],' ',Msg).
1705 get_msg_aux(Type,[],_Info,Msg) :- !,
1706 ajoin(['expanding ', Type],Msg).
1707 get_msg_aux(Type,Parameters,_Info,Msg) :-
1708 ajoin_with_sep(['expanding', Type, 'over ids'|Parameters],' ',Msg).
1709
1710
1711 % check whether an abort error is pending.
1712 pending_abort_error(wfx(_WF0,_WFX,WFE,_)) :- var(WFE),
1713 is_wf_abort_pending(WFE). %print(got_abort_pending),nl.
1714 % succeed once for every pending abort error in Waitflag store
1715 pending_abort_error(wfx(_WF0,_WFX,WFE,_),Msg,Term,Span) :- var(WFE),
1716 is_wf_abort_pending(WFE),
1717 frozen(WFE,Goal), %print(goal(Goal)),nl,
1718 pending_abort_error_aux(Goal,Msg,Term,Span).
1719
1720 pending_abort_error_aux(kernel_waitflags:add_abort_error2(_,_ErrType,Msg,Term,_Result,_ResultValue,Span,_), Msg,Term,Span).
1721 pending_abort_error_aux(add_abort_error2(_,_ErrType,Msg,Term,_Result,_ResultValue,Span,_), Msg,Term,Span).
1722 pending_abort_error_aux(kernel_waitflags:mark_pending_abort_error2(_,Msg,Term,Span), Msg,Term,Span).
1723 pending_abort_error_aux((A,B),Msg,Term,Span) :-
1724 (pending_abort_error_aux(A,Msg,Term,Span) ;
1725 pending_abort_error_aux(B,Msg,Term,Span) ).
1726
1727 % explicitly mark that an abort error is pending
1728 mark_pending_abort_error(wfx(_WF0,_WFX,WFE,_)) :- !, % print(mark_abort_pending(_WFX)),nl,
1729 (var(WFE) -> put_wf_abort_pending(WFE)
1730 ; true).
1731 mark_pending_abort_error(no_wf_available).
1732
1733 % explicitly mark that an abort error is pending with storing information
1734 mark_pending_abort_error(wfx(_WF0,_WFX,WFE,_),Msg,Term,Span) :-
1735 (var(WFE) -> put_wf_abort_pending(WFE),
1736 mark_pending_abort_error2(WFE,Msg,Term,Span)
1737 ; true).
1738 mark_pending_abort_error(no_wf_available,_Msg,_Term,_Span).
1739
1740 :- block mark_pending_abort_error2(-,?,?,?).
1741 mark_pending_abort_error2(_,_,_,_).
1742 % ---------------------------------------------
1743
1744
1745 get_large_finite_wait_flag(Info,WFX,WF2) :-
1746 large_finite_priority(P),
1747 get_wait_flag(P,Info,WFX,WF2).
1748
1749 get_enumeration_starting_wait_flag(Info,WFX,WF2) :-
1750 last_finite_priority(P),
1751 get_wait_flag(P,enumeration_starting(Info),WFX,WF2).
1752 %get_enumeration_almost_finished_wait_flag(Info,WFX,WF2) :-
1753 % max_tagged_integer(P), % largest tagged value
1754 % get_wait_flag(P,enumeration_starting(Info),WFX,WF2).
1755
1756
1757 get_integer_enumeration_wait_flag(Info,WFX,WF2) :-
1758 integer_priority(Prio),
1759 get_wait_flag(Prio,Info,WFX,WF2).
1760
1761 % A few hardcoded priorities
1762 large_finite_priority(P) :- integer_priority(I), P is I-1000.
1763 last_finite_priority(P) :- integer_priority(I), P is I-2. % Note: enumerate_integer_with_min_range_wf did set its priority to I-1
1764 %integer_priority(10000000). % old value
1765 integer_priority(Prio) :- % priority for X : INTEGER enumerations
1766 max_tagged_integer(P),
1767 Prio is P - 1024.
1768 % note inf_type_prio sets some priorities such as seq(INTEGER) ... higher
1769
1770 % priority for sets of infinite type (POW(INTEGER)...)
1771 integer_pow2_priority(Prio) :- % was 10000010
1772 integer_priority(P), Prio is P+10.
1773
1774 last_priority(P) :- % was 10000010
1775 max_tagged_integer(P).
1776
1777 % ensure that we do not exceed priority of type enumeration or use too big numbers
1778 get_bounded_wait_flag(Prio,Info,WF,LWF) :-
1779 get_bounded_priority(Prio,P),
1780 get_wait_flag(P,Info,WF,LWF).
1781
1782 get_bounded_priority(Prio,P) :- number(Prio),
1783 Prio>268435452, % 268435455 is max_tagged_integer on 32 bit platforms
1784 last_finite_priority(MAX),
1785 Prio>MAX,
1786 !, P=MAX.
1787 get_bounded_priority(P,P).
1788
1789 % -------------
1790
1791 % copy waitflags from one waitflag store to another; warning: can ground WF0 from outer store
1792 % the assumption is that the outer store will be grounded/enumerated
1793 % the inner store 1 will now point to the outer store 2 in case some co-routines still add waitflags
1794 copy_waitflag_store(wfx(WF0,Store1,WFE1,_),wfx(WF0,Store2,WFE2,_Info2)) :-
1795 %print(copy_wf(WF0,Store1,Store2,WFE1,WFE2)),nl,
1796 my_get_wf_store_fdvars_atts(Store1,Heap,FDList1),
1797 (FDList1 = [] -> true
1798 ; my_get_fdvars_att(Store2,FDList2),
1799 %append(FDList1,FDList2,NewFDList), % used to be like this
1800 append_wf(FDList2,FDList1,NewFDList), % now we add inner FD variables to end
1801 put_mutable_wf_fdvars_attr(Store2,NewFDList)
1802 ),
1803 avl_to_list(Heap,WF_List),
1804 %print(copying(FDList1,WF_List)),nl,
1805 my_get_wf_store_att(Store2,Heap2),
1806 add_wf(WF_List,Heap2,NewHeap2),
1807 put_mutable_wf_store_attr(Store2,NewHeap2),
1808 set_mutable_ref(Store1,Store2),
1809 % ensures that calls which add waitflags now get-redirected to waitflag store 2
1810 % get_ef from Store2 and add trigger to enumerate Store1?
1811 %portray_waitflags(wfx(WF0,Store2,WFE2,Info2)),
1812 WFE1=WFE2.
1813
1814 append_wf([],FDL,FDL).
1815 append_wf([H|T],FDL2,Res) :-
1816 (ground(H) -> append_wf(T,FDL2,Res) ; Res=[H|RT], append_wf(T,FDL2,RT)).
1817
1818 % Note: add_wf will construct multiple WF variables also for finite_priorities,
1819 % get_waitflag_from_store will only keep multiple entries for infinite ones
1820 add_wf([],Heap,Heap).
1821 add_wf([Prio-wf(WF1,Info)|T],Heap,NewHeap) :-
1822 % purpose: copy waitflag WFlag to WF store; if same priority exists it will be appended at end
1823 P=Prio,
1824 (avl_fetch(P,Heap,wf(WFs,_OldInfo))
1825 -> % priority waitflag(s) exist; add inner store WF1 at end
1826 merge_waitflag_queues(WF1,WFs,MergedWFs), % TO DO: just unify WF 1 and 0.9 or smaller ?
1827 avl_store(P,Heap,wf(MergedWFs,Info),Heap2)
1828 ; % this priority does not yet exist in outer store
1829 avl_store(P,Heap,wf(WF1,Info),Heap2)
1830 ),
1831 add_wf(T,Heap2,NewHeap).
1832
1833
1834 % try and find an exact match for a waitflag info; this is linear in size of waitflag store !
1835 find_waitflag_info(wfx(_WF0,Store,_WFE1,_),Info,Prio,WF1) :-
1836 my_get_wf_store_att(Store,Heap),
1837 avl_member(Prio,Heap,wf(WF1,Info)).
1838
1839 % dereference the waitflag store, see SICStus for SPRM-20503
1840 % should be called e.g., in WHILE loops of interpreter to prevent degradation of put_atts performance
1841 deref_wf(wfx(WF0,S,EF,I),R) :- !, R=wfx(WF0,S2,EF,I), deref_store(S,S2).
1842 deref_wf(WF,WF).
1843
1844 % D is the dereferenced value of V.
1845 % see email by SICStus for SPRM-20503; gets rid of variable chains; was useful for old Waitflag store with attributes
1846 %deref(V,D) :- var(V), !, D = V.
1847 %deref(X,X).
1848
1849 % --------------------------
1850 % DEBUGGING UTILITIES:
1851
1852 % you also need to comment in code above using it
1853 /*
1854 :- dynamic debug_kernel_waitflags/1.
1855 set_debug_kernel_waitflags :- (debug_kernel_waitflags(_) -> true ; assertz(debug_kernel_waitflags(0))).
1856
1857 % the counter allows to set trace points
1858 get_debug_kernel_waitflags(Counter) :- retract(debug_kernel_waitflags(C)), C1 is C+1,
1859 assertz(debug_kernel_waitflags(C1)), Counter=C1.
1860
1861 get_debug_info(Info,Res) :- debug_kernel_waitflags(Counter),!,
1862 (Counter==39 -> trace ; true),
1863 Res=info(Counter,Info).
1864 get_debug_info(Info,Info).
1865 */
1866
1867 % check that a WF is grounded before EWF is grounded:
1868 :- public check_grounding/3. %debugging predicate
1869 check_grounding(WF,Info,LWF) :- get_enumeration_finished_wait_flag(WF,EWF), check_grounding_of_wf(WF,Info,LWF,EWF).
1870 :- block check_grounding_of_wf(?,?,-,-).
1871 check_grounding_of_wf(_,_,LWF,_) :- nonvar(LWF),!.
1872 check_grounding_of_wf(WF,Info,LWF,_EWF) :- add_internal_error('Waitflag not grounded: ',check_grounding(WF,Info,LWF)),
1873 portray_waitflags(WF),nl.
1874
1875 % --------------------------
1876
1877 :- assert_must_succeed(( kernel_waitflags:test_waitflags(1) )).
1878 :- assert_must_succeed(( kernel_waitflags:test_waitflags(2) )).
1879
1880 test_waitflags(1) :-
1881 init_wait_flags(Waitflags,[test_waitflags]),
1882 get_wait_flag(10,Waitflags,WF1),
1883 when(nonvar(WF1),print('WF1 - Prio 10\n')),
1884 get_wait_flag(5,Waitflags,WF2),
1885 when(nonvar(WF2),(print('WF2 - Prio 5\n'),
1886 get_wait_flag(6,Waitflags,WF4),
1887 when(nonvar(WF4),print('WF4 - Prio 6\n')),
1888 get_wait_flag(2,Waitflags,WF5),
1889 when(nonvar(WF5),print('WF5 - Prio2\n')))),
1890 get_wait_flag(5,Waitflags,WF3),
1891 when(nonvar(WF3),print('WF3 - Prio 5\n')),
1892 ground_wait_flags(Waitflags),nl.
1893
1894 test_waitflags(2) :-
1895 init_wait_flags(Waitflags,[test_waitflags]),
1896 get_wait_flag(10,Waitflags,WF1),
1897 when(nonvar(WF1),print('WF1 Prio 10\n')),
1898 get_wait_flag(5,Waitflags,WF2),
1899 when(nonvar(WF2),(print('WF2 - Prio 5\n'),
1900 get_wait_flag(6,Waitflags,WF4),
1901 when(nonvar(WF4),print('WF4 - Prio 6\n')),
1902 get_wait_flag(2,Waitflags,WF5),
1903 when(nonvar(WF5),print('WF5 - Prio2\n')))),
1904 get_wait_flag(5,Waitflags,WF3),
1905 when(nonvar(WF3),print('WF3 - Prio 5\n')),
1906 ground_wait_flags(Waitflags),nl.
1907
1908
1909 /*
1910
1911 bench_waitflags(N) :-
1912 statistics(walltime,[Tot1,_]),
1913 init_wait_flags(Waitflags),
1914 getwf(N,Waitflags),
1915 ground_wait_flags(Waitflags),nl,statistics(walltime,[Tot2,_]), Tot is Tot2-Tot1,
1916 format('Waitflag test for size ~w : ~w ms walltime (wf=~w)]~n',[N,Tot,Waitflags]).
1917 getwf(0,_).
1918 getwf(N,Waitflags) :- N>0, N1 is N-1, get_wait_flag(N,bench(N),Waitflags,_), getwf(N1,Waitflags).
1919
1920 % old attribute based:
1921 | ?- kernel_waitflags:bench_waitflags(1000).
1922
1923 Waitflag test for size 1000 : 37 ms walltime (wf=wfx(0,_205205,0,[]))]
1924 yes
1925 | ?- kernel_waitflags:bench_waitflags(10000).
1926
1927 Waitflag test for size 10000 : 5859 ms walltime (wf=wfx(0,_2512071,0,[]))]
1928 yes
1929
1930 % new mutable based:
1931 | ?- kernel_waitflags:bench_waitflags(1000).
1932
1933 Waitflag test for size 1000 : 4 ms walltime (wf=wfx(0,$mutable(wfm_store(empty,[],[]),3008),0,[]))]
1934 yes
1935 | ?- kernel_waitflags:bench_waitflags(10000).
1936
1937 Waitflag test for size 10000 : 24 ms walltime (wf=wfx(0,$mutable(wfm_store(empty,[],[]),30008),0,[]))]
1938 yes
1939 | ?- kernel_waitflags:bench_waitflags(100000).
1940
1941 Waitflag test for size 100000 : 497 ms walltime (wf=wfx(0,$mutable(wfm_store(empty,[],[]),9),0,[]))]
1942 yes
1943
1944 */