1 % (c) 2009-2025 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, indent_call_stack/1,
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(FDVARS,NextFDVar),
471 % print(enumerating(NextFDVar,FDVARS)),nl, maplist(tools_printing:print_arg,FDVARS),nl,
472 RemainingFDVARS=FDVARS.
473 % ((FDVARS=[H|T],H==NextFDVar) -> RemainingFDVARS=T ; RemainingFDVARS=FDVARS).% does not seem to buy any speed
474
475 get_min_fd_size_elements([V1|FDVARS],MinFDVars) :- clpfd_size(V1,Size),
476 get_min_aux(FDVARS,Size,[V1],MinFDVars).
477
478 get_min_aux([],_,MinFDVars,MinFDVars).
479 get_min_aux([V1|FDVARS],Size,MAcc,MinFDVars) :- number(V1),!,
480 get_min_aux(FDVARS,Size,MAcc,MinFDVars).
481 get_min_aux([V1|FDVARS],Size,MAcc,MinFDVars) :-
482 clpfd_size(V1,Size1),
483 (lt_size(Size1,Size)
484 -> get_min_aux(FDVARS,Size1,[V1],MinFDVars)
485 ; Size1=Size -> get_min_aux(FDVARS,Size,[V1|MAcc],MinFDVars)
486 ; get_min_aux(FDVARS,Size,MAcc,MinFDVars)
487 ).
488
489 lt_size(X,Y) :- number(X),(Y=sup -> true ; X<Y).
490
491 % -------------
492
493 % using this is relevant for e.g. tests 415, 416, 1096 : to give variables without frozen goals a lower priority
494 % TO DO: we could filter out those before we call clpfd_get_next_variable_to_label !?
495 fd_priority_leq_limit(FDVAR,Prio,Limit) :-
496 clpfd_size(FDVAR,Size),
497 number(Size), % not =sup or =inf
498 geq_limit(Limit,Size), % the calculation below will only increase the Size
499 (Size < 2 -> Prio = Size
500 ; geq_limit(Limit,Size*4) -> Prio = Size % avoid calling frozen and fd_degree, Priority only used for warnings and bisect decisions
501 ; (enumeration_only_fdvar(FDVAR) -> Prio is Size*4, Prio =< Limit
502 ; Prio = Size)
503 ).
504
505 gt_limit(inf,_) :- !.
506 gt_limit(inf_overflow,_) :- !.
507 gt_limit(sup,_) :- !.
508 gt_limit(X,Limit) :- X > Limit.
509
510 geq_limit(inf,_) :- !.
511 geq_limit(inf_overflow,_) :- !.
512 geq_limit(sup,_) :- !.
513 geq_limit(X,Limit) :- X >= Limit.
514
515
516
517 % -------------
518
519
520 enumeration_only_fdvar(FDVAR) :- var(FDVAR),fd_degree(FDVAR,D),!,
521 D=0,
522 frozen(FDVAR,Goal), % print(frozen(FDVAR,D,Goal)),nl,
523 enum_only_frozen_goal(Goal,FDVAR).
524 enumeration_only_fdvar(_).
525
526 % TO DO: maybe use enumeration_only_goal in b_enumerate
527 enum_only_frozen_goal((A,B),Var) :- !, enum_only_frozen_goal(A,Var), enum_only_frozen_goal(B,Var).
528 enum_only_frozen_goal(true,_).
529 enum_only_frozen_goal(Module:Call,Var) :- enum_only_frozen_goal_m(Module,Call,Var).
530
531 enum_only_frozen_goal_m(clpfd,_,_) :- !.
532 enum_only_frozen_goal_m(kernel_objects,G,V) :- !, enum_only_frozen_goal_k_obj(G,V).
533 enum_only_frozen_goal_m(b_interpreter_components,observe_variable_block(_,_,_,_,_),_).
534 enum_only_frozen_goal_m(b_interpreter_components,observe_variable1_block(_,_,_,_),_).
535 enum_only_frozen_goal_m(custom_explicit_sets,block_copy_waitflag_store(_,_,_,_,_),_).
536
537 enum_only_frozen_goal_k_obj(safe_less_than_equal(_,X,Y),_) :- (number(X);number(Y)), !.
538 enum_only_frozen_goal_k_obj(call_enumerate_int(X,_,_,_),Var) :- Var==X,!.
539 enum_only_frozen_goal_k_obj(enumerate_int_wf(X,_,_,_,_),Var) :- Var==X,!.
540 enum_only_frozen_goal_k_obj(true,_).
541
542
543 % get a wait-flag that will be triggered first next time;
544 % can be used to ensure that all pending co-routines complete
545 get_idle_wait_flag(Info,wfx(WF0,Store,EnumFinished,_),LWF) :- !,
546 (var(WF0) -> LWF=WF0
547 ; WF0=s(WF00), var(WF00) -> LWF=WF00
548 ; nonvar(EnumFinished) -> LWF=1 % waitflag store already grounded to completion; nobody may drive it anymore
549 ; get_waitflag_from_store(0.9,Info,Store,LWF)
550 ).
551 get_idle_wait_flag(_Info,no_wf_available,1).
552
553
554 get_wait_flag(Prio,Store,WF) :- get_wait_flag(Prio,'??',Store,WF).
555 get_wait_flag(Prio,_Info,wfx(WF0,_Store,EnumFinished,_),WF) :- (Prio=0 ; ground(EnumFinished)),
556 !, % if EnumFinished is ground nothing will drive the Waitflag store anymore
557 % WF0 can be set to a variable by clone_wait_flags_from1
558 WF=WF0.
559 get_wait_flag(1.0,Info,wfx(WF0,Store,_EnumFinished,_),WF) :-
560 % WF for deterministic computations, not guaranteed to generate efficient representations
561 % (those are triggered in WF0 phase)
562 !,
563 %used to b: (var(WF0) -> get_waitflag_from_store(1.0,Info,Store,WF) ; WF=1).
564 (ground(WF0) % if WF0 is ground the WF0 phase is completed fully;
565 -> WF=1 % we can return this waitflag immediately;
566 % good idea also if we are already in the later stages of propagation
567 % so as not to delay the attached deterministic propagations (see test 383, SudokuHexAsConstant.mch)
568 ; get_waitflag_from_store(1.0,Info,Store,WF)).
569 get_wait_flag(Prio,Info,wfx(_,Store,EnumFinished,_),WF) :- !,
570 (ground(EnumFinished) -> WF=Prio /* enumeration has finished: return a ground WF */
571 ; get_waitflag_from_store(Prio,Info,Store,WF)).
572 get_wait_flag(Prio,Info,Store,WF) :- check_is_wait_flag(Store),
573 (Store=no_wf_available -> WF=Prio ;
574 Store=none -> print(get_wait_flag_deprecated(Prio,Info,Store,WF)),nl, WF=Prio ;
575 add_internal_error('Illegal call: ',get_wait_flag(Prio,Info,Store,WF))).
576
577 :- use_module(kernel_card_arithmetic,[is_inf_or_overflow_card/1]).
578 % get Waitflag with Priority Prio, additional information Info from Store
579 get_waitflag_from_store(Prio,Info,Store,WF) :-
580 % print(get_waitflag_from_store(Prio,Info,Store,WF)),nl,
581 (is_inf_or_overflow_card(Prio) -> integer_priority(RealPrio)
582 ; RealPrio=Prio),
583 my_get_wf_store_att(Store,Heap),
584 (avl_fetch(RealPrio,Heap,wf(WFs,_OldInfo)) %,print(fetch(RealPrio,WFs,_OldInfo)),nl
585 -> ((RealPrio < 2 %is_finite_priority(RealPrio)
586 ; Info = allow_reuse(_))
587 %RealPrio < 100000 % TO DO: investigate performance if reduce the threshold
588 % here we re-use the waitflag instead of storing a new one
589 -> (var(WFs) -> WF1 = WFs %, print(reusing_wf(RealPrio,WF1,Info)),nl
590 ; WFs = [WF1|_]-_ -> true % add_wf has constructed a difference list
591 ; print(wf_already_grounded(Prio,Info,WFs)),nl,
592 WFs = WF1
593 )
594 ; push_waitflag(WFs,WF1,WFs2) ->
595 % poses problems for 1003, possibly 1194; keep WFs as a FIFO stack to have localised enumeration
596 %(RealPrio<100000 -> test_runner:inc_test_target_coverage ; true),
597 avl_store(RealPrio,Heap,wf(WFs2,Info),NewHeap),
598 put_mutable_wf_store_attr(Store,NewHeap)
599 ; add_internal_error('Pushing waitflag failed:',RealPrio:WFs)
600 )
601 ; avl_store(RealPrio,Heap,wf(WF1,Info),NewHeap), % print(storing_fresh_wf(RealPrio,WF1,Info)),nl,
602 put_mutable_wf_store_attr(Store,NewHeap) % put_atts seems to be expensive
603 ),!,
604 WF=WF1.
605 get_waitflag_from_store(Prio,Info,Store,WF) :-
606 add_internal_error('Error getting Waitflag: ', get_waitflag_from_store(Prio,Info,Store,WF)),fail.
607
608 % ----------------
609
610 % utilities for managing difference lists of waitflag variables for a particular priority:
611
612 % for each priority we store wf(WFQueue,Info)
613 % WFQueue is either a free variable (aka waitflag), or a difference list of free variables (waitflags)
614
615 % push new waitflag NextWF onto an existing WF list for a priority
616 push_waitflag(WFs,NextWF,NewWFs) :- var(WFs),!, NewWFs = [WFs,NextWF|TAIL]-TAIL. % create difference list
617 push_waitflag(Head-TAIL,NextWF,NewWFs) :- !, TAIL = [NextWF|NewTAIL], NewWFs = Head-NewTAIL.
618 push_waitflag(WFs,_,WFs) :- add_internal_error('Illegal waitflag list:',WFs).
619
620 % pop the next waitflag NextWF from a list of waitflags associated with some priority
621 pop_waitflag(WFs,NextWF,Tail) :- var(WFs),!, NextWF=WFs,Tail=[].
622 pop_waitflag(Head-Tail,NextWF,NextTail) :- Head \== Tail, % should always succeed
623 !,
624 Head = [NextWF|NextHead],
625 (NextHead==Tail -> NextTail=[] ; NextTail = NextHead-Tail).
626 pop_waitflag(WFs,_,_) :- add_internal_error('Illegal waitflag list, cannot pop:',WFs),fail.
627
628 % merge_waitflag_queues(InnerWF,OuterWF,MergedWF)
629 merge_waitflag_queues(WF1,WFs,MergedWFs) :- var(WFs),!,
630 push_waitflag(WF1,WFs,MergedWFs).
631 merge_waitflag_queues(WF1,Head2-Tail2,MergedWFs) :- var(WF1),!,
632 MergedWFs = [WF1|Head2]-Tail2. % add WF1 at front
633 merge_waitflag_queues(Head1-Tail1,Head2-Tail2,MergedWFs) :- !, % concatenation of difference lists
634 Tail1=Head2,
635 MergedWFs = Head1-Tail2.
636 merge_waitflag_queues(WF1,WFs,MergedWFs) :-
637 add_internal_error('Illegal WF list:',merge_waitflag_queues(WF1,WFs,MergedWFs)),
638 MergedWFs = WFs.
639
640
641 % ------------------------
642
643 % a version which delays getting a waitflag until the Prio is known:
644 :- block block_get_wait_flag(-,?,?,?).
645 block_get_wait_flag(Prio,Info,WFX,WF) :- get_wait_flag(Prio,Info,WFX,WF).
646
647 % check if there are no waitflags and no FD variables; note: there could be pending co-routines on WF0
648 no_pending_waitflags(no_wf_available).
649 no_pending_waitflags(wfx(_WF0,Store,_WFE,_)) :-
650 my_get_wf_store_fdvars_atts(Store,Heap,FDList),
651 FDList=[], % no FD Variables
652 empty_avl(Heap). % no waitflags pending
653 % Note grounding could still be in progress
654 % TO DO: we could add attribute before grounding and remove after grounding !?
655
656
657 % -------------------
658
659 % storing Predicates for Kodkod external function:
660 my_get_kodkod_predicates(wfx(_,Store,_,_),ID,PredicateList) :- my_get_wf_preds(Store,ID,kodod,PredicateList).
661 my_get_wf_preds(Store,ID,Type,PredicateList) :-
662 (my_get_wf_preds_att(Store,ID,Type,PredicateList)
663 -> true
664 ; PredicateList = []
665 ).
666 %my_store_wf_kodkod(Store,ID,PredicateList) :- put_atts(Store,+wf_kodkod(ID,PredicateList)).
667 my_add_predicate_to_kodkod(wfx(_,Store,_,_),ID,Predicate) :-
668 my_add_predicate_aux(wfx(_,Store,_,_),ID,kodod,Predicate).
669 my_add_predicate_aux(wfx(_,Store,_,_),ID,Type,Predicate) :-
670 my_get_wf_preds(Store,ID,Type,PredicateList),
671 %length(PredicateList,Len), format('Adding pred to ~w of len ~w~n',[ID,Len]),
672 put_mutable_other_attr(Store,wf_preds(ID,Type,[Predicate|PredicateList])).
673
674 % -------------------
675
676 % storing Predicates for external function calling satsolver:
677 my_get_satsolver_predicates(wfx(_,Store,_,_),ID,PredicateList) :-
678 my_get_wf_preds(Store,ID,satsolver,PredicateList).
679 my_add_predicate_to_satsolver(wfx(_,Store,_,_),ID,Predicate) :-
680 my_add_predicate_aux(wfx(_,Store,_,_),ID,satsolver,Predicate).
681
682 % -------------------
683
684 % allow to store and update other information entries
685 % they are mixed in with the Kodkod predicate list
686
687 get_wf_all_dynamic_infos(no_wf_available,[]).
688 get_wf_all_dynamic_infos(wfx(_,Store,_,_),List) :- my_get_all_wf_info_atts(Store,List).
689
690 get_wf_dynamic_info(wfx(_,Store,_,_),ID,Val) :- my_get_wf_info_att(Store,ID,Val).
691
692 put_wf_dynamic_info(no_wf_available,_,_).
693 put_wf_dynamic_info(wfx(_,Store,_,_),ID,Val) :- put_mutable_other_attr(Store,wf_info(ID,Val)).
694
695 % -------------------
696
697
698 ?ground_wait_flag0(wfx(WF0,_,_,_)) :- grd_wf0(WF0).
699
700 ground_wait_flags(wfx(WF0,Store,EnumFinish,WFInfos)) :- !,
701 ? grd_wf0(WF0),
702 deref_store(Store,DStore),
703 init_wf_infos_for_grounding(WFInfos,WFInfos2),
704 %nl,print(' ground_wait_flags:'),nl,portray_waitflags_store(Store), portray_call_stack(wfx(WF0,Store,EnumFinish,WFInfos)),nl,
705 ? ground_waitflags_store_clpfd(DStore,WFInfos2),
706 grd_ef(EnumFinish).
707 ground_wait_flags(no_wf_available) :- !.
708 ground_wait_flags(W) :-
709 add_internal_error('Waitflags in wrong format: ',ground_wait_flags(W)).
710
711 grd_wf0(E) :- %print(start_grd_wf0(E)),nl,
712 (var(E) ->
713 E=s(E2), % first make nonvar, and then ground (separate call to avoid merging unifications)
714 ? grd_wf01(E2) %%,print(grd_wf0_done),nl
715 ; E=s(E2) -> (nonvar(E2),E2 \== 0 -> add_internal_error('Illegal WF0 Waitflag value: ',grd_wf0(E))
716 ; grd_wf01(E2))
717 ; add_internal_error('Illegal WF0 Waitflag value functor: ',grd_wf0(E))
718 ).
719 grd_wf01(0).
720
721 grd_ef(E) :- var(E) -> E=0
722 ; ((E==0;E==wd_guarded) -> true ; add_internal_error('Illegal EF Waitflag value: ',grd_ef(E))).
723
724 /* currently unused:
725 create_wdguarded_wait_flags(wfx(WF0,Store,EnumFinish,Info),Res,Expected,
726 wfx(WF0,Store,LocalEnumFinish,Info)) :-
727 %% if Res=Expected then the computations of the inner waitflags should be well-defined;
728 %% otherwise they may not
729 copy_wfe(EnumFinish,Res,Expected,LocalEnumFinish).
730 :- block copy_wfe(-,?,?,?), copy_wfe(?,-,?,?).
731 copy_wfe(EnumFinish,Res,Expected,LocalEnumFinish) :-
732 (Res==Expected -> LocalEnumFinish=EnumFinish
733 ; LocalEnumFinish=wd_guarded %% branch is pruned by well-definedness condition
734 ).
735 */
736
737 % ground_constraintprop_wait_flags will not ground WFE flag
738
739 :- load_files(library(system), [when(compile_time), imports([environ/2])]).
740 :- if(environ(prob_safe_mode,true)).
741 ground_constraintprop_wait_flags(W) :- check_is_wait_flag(W),fail.
742 :- endif.
743 ground_constraintprop_wait_flags(wfx(WF0,Store,_WFE,WFInfos)) :-
744 %% print(ground_constraintprop_wait_flags(WF0,Store,_WFE)),nl, %%
745 ? !,grd_wf0(WF0),% print(finished_grounding_wf0),nl,portray_waitflags_store(Store),
746 deref_store(Store,DStore),
747 init_wf_infos_for_grounding(WFInfos,WFInfos2),
748 ? ground_waitflags_store_clpfd(DStore,WFInfos2).
749 ground_constraintprop_wait_flags(no_wf_available) :- !.
750 ground_constraintprop_wait_flags(W) :-
751 add_internal_error('Waitflags in wrong format',ground_constraintprop_wait_flags(W)).
752
753
754
755 :- use_module(clpfd_interface,[clpfd_in_domain/1, clpfd_labeling/2]).
756 :- use_module(library(lists),[reverse/2]).
757
758 % deprecated:
759 ground_wf(Prio,WF) :-
760 (var(WF) -> WF=Prio ; if(WF=Prio,true,add_internal_error('Illegal waitflag: ',ground_wf(Prio,WF)))).
761
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 % print debug info when a CLP(FD) variable gets labeled/grounded
778 ground_clpfd_variable(Prio,Variable) :- var(Variable),!,
779 clpfd_size(Variable,Size),
780 format(' -> label ~w (prio=~w, clpfd-size=~w)~n',[Variable,Prio,Size]).
781 ground_clpfd_variable(_,_).
782
783 :- use_module(library(terms),[term_hash/3]).
784 ground_prio(Prio,WFMin,Info,WFInfos) :- WFInfos \= '$debug_done',
785 (member(IM,WFInfos),get_info_context_description(IM,Msg) -> true ; Msg=''),
786 nl,indent_wf(WFInfos),
787 term_hash(Info,[if_var(ignore)],Hash),
788 format(' -> ~w --> #~w# ~w :: ~w ~w~n',[Prio,Hash,Info,WFMin,Msg]),
789 !,
790 ground_prio(Prio,WFMin,Info,'$debug_done'). % recursive, but this time normal clauses will apply
791 :- else.
792 init_wf_infos_for_grounding(I,I).
793 ground_clpfd_variable(_,_).
794 :- endif.
795 ground_prio(Prio,WF,Info,WFInfos) :- %print(ground_prio(Prio,WF,Info)),nl,
796 nonvar(WF),!,
797 (WF=[H|T] -> % we used sometimes to have a list of waitflags; ground one-by one;
798 add_internal_error('Deprecated grounding of lists:',ground_prio(Prio,WF,Info,WFInfos)),
799 reverse([H|T],Ls),
800 maplist(ground_wf(Prio),Ls)
801 ; ground(WF) -> true % already grounded by somebody else
802 ; add_internal_error('Waitflag already partially grounded: ',ground_prio(Prio,WF,Info))).
803 %ground_prio(Prio,WF,Info,WFInfos) :- get_debug_kernel_waitflags(Cntr),!,
804 % print(ground_prio(Cntr,Prio,WF,Info)),nl, WF=Prio, print(grounded_prio(Cntr,Prio,WF,Info)),nl.
805 ground_prio(Prio,WF,_Info,_WFInfos) :-
806 %external_functions:indent_format(user_output,'~w (~w)~n',[Prio,Info],no_trace),portray_attached_goals(WF),nl,
807 %% (debug:debug_mode(on) -> external_functions:indent_format(user_output,'~w (~w)~n',[Prio,Info],no_trace) ; true),
808 % print(ground(Prio,WF,Info)),nl, translate:print_frozen_info(WF),nl,
809 WF = Prio. % ground waitflag
810 % print(grounded(Prio,Info)),nl,nl, %%
811
812
813 label_clpfd_variable(Variable) :- label_clpfd_variable(2097153,Variable).
814 label_clpfd_variable(Prio,Variable) :-
815 ground_clpfd_variable(Prio,Variable), % print debug info
816 (gt_limit(Prio,2097152), % note Prio > test also works with float(X) terms !
817 check_if_labeling_domain_large(Variable,2097152,Size,RANGE)
818 % {x|x:1..2**23 & x mod 2 = x mod 1001} takes about 2 minutes;
819 % {x|x:1..2**21 & x mod 2 = x mod 1001} takes about 30 seconds; 2**21 = 2097152, 2**23 = 8388608
820 -> %enum_warning_large(Variable,'INTEGER VARIABLE',RANGE), % could be enumerated set; but unlikely given size;
821 % enum_warning will generate virtual time-outs, ... -> we no longer do this;
822 % for AMASS scheduler.mch there is a time-out after step 301 otherwise
823 performance_messages:perfmessage(enumerating_large_integer_variable(RANGE)),
824 (number(Size)
825 -> clpfd_labeling([Variable],[bisect])
826 % new option in SICS 4.10: interval; ffc no longer required: just a single variable
827 ; add_warning(label_clpfd_variable,'Trying to enumerate FD Variable with infinite domain (possibly due to WD error) ',RANGE)
828 % or due to clpfd_tables element/3 adding unbounded variables for labeling (add_fd_variables_for_labeling)
829 )
830 % we use bisecting rather than default enumeration; e.g.,
831 % x:1..8388610 & y:1..8388610 & x*x*x = y*y + y + 10 is relatively quickly determined to be unsat (380 ms)
832 % Note: test 1099 tests this
833 % TO DO: investigate whether it makes sense to use bisect for smaller values already
834 ? ; clpfd_in_domain(Variable) % also does random if randomise_enumeration_order set
835 ).
836
837 :- use_module(clpfd_interface,[ clpfd_size/2]).
838 check_if_labeling_domain_large(H,Limit,Size,Res) :-
839 clpfd_size(H,Size),
840 gt_limit(Size,Limit),
841 clpfd_interface:clpfd_domain(H,From,To),
842 Res=From:To.
843
844 %check_if_labeling_domain_finite(H) :-
845 % clpfd_size(H,Size), number(Size).
846
847 % mix ProB labeling with CLP(FD) labeling
848 ground_waitflags_store_clpfd(Store,WFInfos) :-
849 my_get_wf_store_fdvars_atts(Store,Heap,FD),!,
850 ? ground_waitflags_store_clpfd_aux(Store,Heap,FD,WFInfos).
851 ground_waitflags_store_clpfd(Store,WFInfos) :-
852 add_internal_error('Illegal Waitflag: ',ground_waitflags_store_clpfd(Store,WFInfos)),fail.
853
854 ground_waitflags_store_clpfd_aux(Store,Heap,FDVars,WFInfos) :- FDVars \= [],
855 %my_get_fdvars_att(Store,FDVars),
856 %get_atts(Store,wf_fdvars(FDVars)), % there is a list of FD vars available
857 !,
858 ( avl_del_min_waitflag(Heap,Prio,wf(WFMin,Info),NewHeap)
859 -> % we have non-ground waitflags with a matching priority
860 (my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS),
861 fd_priority_leq_limit(NextFDVar,FDPrio,Prio)
862 ? -> enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS,WFInfos)
863 ; put_mutable_wf_store_attr(Store,NewHeap), % write the modified heap back
864 % debug:hit_counter(XXX),print(grounding(XXX,Prio,_Info)),nl, (XXX=16556->trace ; true),
865 ? ground_prio(Prio,WFMin,Info,WFInfos),
866 ? ground_waitflags_store_clpfd(Store,WFInfos) % continue recursively
867 )
868 ; % there are no pending waitflag
869 (my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS)
870 -> enum_fd_variable_store(Store,NextFDVar,RemainingFDVARS,WFInfos)
871 ; true % heap and FDVars list is empty, nothing more to do
872 )
873 ).
874 ground_waitflags_store_clpfd_aux(Store,Heap,[],WFInfos) :- % no FDVars are available to enumerate
875 avl_del_min_waitflag(Heap,Prio,wf(WFMin,Info),NewHeap),
876 !,
877 put_mutable_wf_store_attr(Store,NewHeap), % write the modified heap back
878 % debug:hit_counter(XXX),print(grounding(XXX,Prio,_Info)),nl, (XXX=16556->trace ; true),
879 ? ground_prio(Prio,WFMin,Info,WFInfos), % ground waitflag
880 ? ground_waitflags_store_clpfd(Store,WFInfos). % continue recursively
881 ground_waitflags_store_clpfd_aux(_Store,_Heap,[],_). % heap and FDVars list is empty, nothing more to do
882
883 %:- use_module(library(lists),[exclude/3]).
884 enum_fd_variable_store(Store,NextFDVar,RemainingFDVARS,WFInfos) :-
885 %exclude(nonvar,RemainingFDVARS,RemainingFDVARS2),
886 put_mutable_wf_fdvars_attr(Store,RemainingFDVARS),
887 %% print(labeling(NextFDVar,RemainingFDVARS)),nl, portray_fd_vars([NextFDVar|RemainingFDVARS]),nl,nl,
888 label_clpfd_variable(NextFDVar),
889 ground_waitflags_store_clpfd(Store,WFInfos).
890 % a version of the above where we know the priority
891 enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS,WFInfos) :-
892 %exclude(nonvar,RemainingFDVARS,RemainingFDVARS2), % does not seem to buy a lot
893 put_mutable_wf_fdvars_attr(Store,RemainingFDVARS),
894 %% print(labeling(NextFDVar,RemainingFDVARS)),nl, portray_fd_vars([NextFDVar|RemainingFDVARS]),nl,nl,
895 ? label_clpfd_variable(FDPrio,NextFDVar),
896 ? ground_waitflags_store_clpfd(Store,WFInfos).
897
898 :- if(false).
899 % small utility to trace through waitflag enumeration
900 :- dynamic spying_on/1.
901 spying_on(1).
902 spy_waitflags(_,_) :- \+ spying_on(_) , !.
903 spy_waitflags(Msg,Store) :- retract(spying_on(Nr)),print(Msg),nl,
904 (Nr=1
905 -> assertz(spying_on(Nr)),
906 print(' (j,t,p,q) ==> '),read_term(T,[]),
907 action(T,Store)
908 ; N1 is Nr-1, assertz(spying_on(N1))
909 ).
910 action(t,_Store) :- !,trace.
911 action(p,Store) :- !,portray_waitflags_store(Store), spy_waitflags('',Store).
912 action(q,_Store) :- !,retractall(spying_on).
913 action(Nr,_) :- number(Nr),!, retractall(spying_on(_)), assertz(spying_on(Nr)).
914 action(_,_).
915 :- endif.
916
917 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF), kernel_waitflags:portray_waitflags(WF),
918 kernel_waitflags:get_wait_flag(2,'Test',WF,WF2), when(nonvar(WF2),print(wf2)),
919 FD in 1..2,kernel_waitflags:add_fd_variable_for_labeling(FD,WF),
920 kernel_waitflags:portray_waitflags(WF), kernel_waitflags:ground_wait_flags(WF) )).
921 % PORTRAYING Waitflags
922 portray_waitflags(wfx(WF0,Store,WFE,Infos)) :- flush_output,
923 delete(Infos,call_stack(_),Infos2),
924 print('*WAITFLAG STORE: '),print(wfx(WF0,'STORE',WFE,Infos2)),nl,
925 portray_wait_flag_infos(Infos),nl,
926 (var(WF0) -> print('> WF0 :: '), portray_attached_goals(WF0),nl ; true),
927 portray_waitflags_store(Store),!,
928 (var(WFE) -> print('> WFE :: '), portray_attached_goals(WFE),nl ; true),
929 print('*END WAITFLAG STORE'),nl.
930 %my_portray_avl(Heap),nl.
931 portray_waitflags(none) :- !, print('*WAITFLAG STORE: none'),nl.
932 portray_waitflags(no_wf_available) :- !, print('*WAITFLAG STORE: none'),nl.
933 portray_waitflags(Store) :-
934 add_internal_error('Illegal Waitflag: ',portray_waitflags(Store)),flush_output.
935
936 portray_fd_vars([]) :- !, '$NOT_COVERED'('This is debugging code!').
937 portray_fd_vars([V|T]) :- print('> '), print(V), nonvar(V),!, nl,
938 portray_fd_vars(T), '$NOT_COVERED'('This is debugging code!').
939 portray_fd_vars([V|T]) :- clpfd_size(V,Size), !, print(' : '), print(Size),
940 portray_attached_goals(V),
941 portray_fd_vars(T),
942 '$NOT_COVERED'('This is debugging code!').
943 portray_fd_vars(X) :- print('> *** UNKNOWN FDVARS: '), print(X), nl,
944 '$NOT_COVERED'('This is debugging code!').
945
946 portray_waitflags_store(Store) :-
947 my_get_fdvars_att(Store,FDList),
948 (FDList=[] -> true ; print('FDVARS:'),nl,portray_fd_vars(FDList),nl),
949 my_get_wf_store_att(Store,Heap),
950 avl_to_list(Heap,List), portray_avl_els(List).
951
952 portray_avl_els([]).
953 portray_avl_els([Prio-wf(LWF,Info)|T]) :-
954 print('> '),print(Prio), print(' : '), print(LWF), print(' : '), portray_info(Info),nl,
955 portray_attached_goals(LWF),
956 portray_avl_els(T).
957
958 portray_info(I) :- print(I).
959
960 portray_attached_goals(V) :- V==[],!.
961 portray_attached_goals(LWF) :- nonvar(LWF),LWF=[H|T],!,
962 portray_attached_goals(H),
963 portray_attached_goals(T).
964 portray_attached_goals(LWF) :- frozen(LWF,Goal), print(' :: '), portray_goal(Goal),nl.
965
966 :- use_module(tools_printing,[print_term_summary/1]).
967 portray_goal((A,B)) :- !, portray_goal(A), print(','), print(' '), portray_goal(B).
968 portray_goal(A) :- print_term_summary(A).
969
970
971 :- public my_portray_avl/1.
972 my_portray_avl(V) :- var(V), !, add_internal_error('Variable: ',my_portray_avl(V)).
973 my_portray_avl(V) :- portray_avl(V).
974
975
976 quick_portray_waitflags(wfx(WF0,Store,WFE,_)) :-
977 my_get_wf_store_fdvars_atts(Store,Heap,FDList),
978 avl_domain(Heap,List),
979 format('% Waitflags-Store WF0=~w,WFE=~w, Flags=~w, FDVars=~w~n',[WF0,WFE,List,FDList]).
980 quick_portray_waitflags(no_wf_available) :-
981 format('% Waitflags-Store no_wf_available~n',[]).
982 quick_portray_waitflags_store(Store) :-
983 my_get_wf_store_fdvars_atts(Store,Heap,FDList),
984 avl_domain(Heap,List),
985 format('% Waitflags-Store Flages=~w, FDVars=~w~n',[List,FDList]).
986
987 % get information about the phase a Waitflag store is in
988 get_waitflags_phase(no_wf_available,Phase) :- Phase=no_wf_available.
989 get_waitflags_phase(wfx(WF0,_,WFE,_),Phase) :-
990 (var(WF0) -> Phase=phase0
991 ; \+ ground(WF0) -> Phase=phase1
992 ; ground(WFE) -> Phase=enumeration_finished
993 ; Phase = grounding). % TODO: get avl_min
994
995 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF),
996 kernel_waitflags:get_wait_flag1(WF,WF1), when(ground(WF1),X=a),
997 kernel_waitflags:ground_wait_flags(WF), X==a )).
998
999 % create a copy of the waitflags with WF0 and WFE as variable; should be finished with copy_wf_finish
1000 % can be used if you temporarily want to disable non-deterministic enumeration, ...
1001 % This is typically used within e.g., a local scope where a conjunction of predicates is added
1002 % we start 1) by doing copy_wf_start(OuterWF,LocalWF), 2) assert all predicates,
1003 % and then 3) call copy_wf_finish at the end of the block
1004 % This ensures that during assertion of the predicates only efficient deterministic computations occur
1005 copy_wf_start(wfx(_,WFStore,_,Infos),Info,wfx(_,WFStore,_,NewInfos)) :-
1006 add_debug_wait_flag_info(Infos,wf_copy(Info),NewInfos).
1007 copy_wf_start(no_wf_available,_,no_wf_available).
1008
1009 % just copy WF0 and WFE over to new waitflag
1010 copy_wf_finish(wfx(WF0,_,WFE,_),wfx(WF0,Store,WFE2,Info)) :-
1011 ? (var(WFE) -> WFE2 = WFE
1012 ; % the outer WF is already fully grounded and will not be driven anymore
1013 % so now we ground all the constraints that have been set-up since copy_wf_start
1014 ground_wait_flags(wfx(WF0,Store,WFE2,Info))
1015 ).
1016 copy_wf_finish(no_wf_available,no_wf_available).
1017
1018
1019 /* create a copy where the enumeration_finished waitflag is shared;
1020 the inner enumeration_finished wait flags should normally not be grounded */
1021 create_inner_wait_flags(wfx(_,OuterStore,WFE,Infos),Info,wfx(_WF0Inner,S,WFEInner,NewInfos)) :-
1022 add_debug_wait_flag_info(Infos,Info,NewInfos),
1023 copy_wfe_to_inner(WFE,WFEInner),
1024 my_get_all_wf_info_atts(OuterStore,DynInfos), % copy dynamic wf_infos from outer to inner store
1025 init_wait_flags_store_with_dynamic_infos(S,DynInfos).
1026
1027 create_inner_wait_flags(no_wf_available,_,no_wf_available).
1028
1029 % was using: get_inner_enumeration_over_wait_flag/get_enumeration_almost_finished_wait_flag
1030 %:- block copy_wfe_to_inner(-,?,?).
1031 %copy_wfe_to_inner(_,WFE,WFE).
1032
1033 :- block copy_wfe_to_inner(-,-).
1034 copy_wfe_to_inner(WFE,_) :- var(WFE),!. % inner was grounded before outer
1035 copy_wfe_to_inner(WFE,WFE).
1036 % copy enumeration finished waitflag from inner computation if WFE was not grounded there
1037 % assumes inner WF enumeration is finished
1038 copy_wfe_from_inner_if_necessary(wfx(_,_,WFEInner,_),wfx(_,_,WFE,_)) :- var(WFEInner),!, WFE=WFEInner.
1039 copy_wfe_from_inner_if_necessary(_,_).
1040
1041 % is very similar to ground_constraintprop_wait_flags, but will also ground EF if no abort is pending
1042 ground_inner_wait_flags(WF) :-
1043 ? ground_constraintprop_wait_flags(WF),
1044 ground_ef_wait_flag_unless_abort_pending(WF).
1045
1046 % copy WF0 and WFE, set up a new store for the rest
1047 copy_wf01e_wait_flags(wfx(WF0,WFX,WFE,Infos),wfx(WF0,S,WFE,Infos)) :-
1048 init_wait_flags_store(S),
1049 get_waitflag_from_store(1,copy_wf01e,WFX,WF1), % now ground wait flag 1 if it is grounded in the orginal WF store
1050 copy_wf01e_wait_flags_aux(WF1,S).
1051 copy_wf01e_wait_flags(no_wf_available,no_wf_available).
1052
1053 :- block copy_wf01e_wait_flags_aux(-,?).
1054 copy_wf01e_wait_flags_aux(_,S) :-
1055 ground_waitflags_store_up_to_no_clpfd(1,S,[]). % no CLPFD - labeling required until prio 1
1056
1057 %--------------
1058
1059 % the following is useful when new predicates are checked during enumeration
1060 % creates a copy of the wait_flags, except for WF0; this needs to be grounded separately with grd_wf0/1 or clone_finish
1061 % thanks to the copy, WF0 is a variable again and we give priority to propagations that create avl_sets
1062 clone_wait_flags_from1(wfx(_WF0,S,WFE,Infos),Ground,wfx(_NewWF0,S2,NewWFE,Infos)) :-
1063 (nonvar(WFE)
1064 -> Ground=ground_upon_finish,
1065 add_debug_message(clone_wait_flags_from1,'Cloning WF store whose enumeration is finished: ',Infos)
1066 ; Ground=no_grounding,
1067 NewWFE=WFE % waitflag store still being driven by enumerator, no need to ground/enumerate afterwards
1068 ),
1069 deref_store(S,S2).
1070 clone_wait_flags_from1(no_wf_available,no_grounding,no_wf_available).
1071
1072 % call after clone_wait_flags_from1, after you have set up all predicates/constraints
1073 clone_wait_flags_from1_finish(wfx(WF0,_,_,_),Ground,wfx(WF0,S2,NewWFE,Infos)) :- % finish copying by copying over WF0
1074 ? (Ground=no_grounding -> true
1075 ; ground_wait_flags(wfx(WF0,S2,NewWFE,Infos)) % the cloned store is not being enumerated anymore; do it ourselves
1076 ).
1077 clone_wait_flags_from1_finish(no_wf_available,_,no_wf_available).
1078
1079 %--------------
1080
1081 get_wait_flag0(wfx(WF0,_,_,_),WF0).
1082 get_wait_flag0(no_wf_available,0).
1083
1084 %get_wait_flag0(WFX,WF0) :- get_wait_flag(0,WFX,WF0). /* when this is ground you can do det. propagatons */
1085 get_wait_flag1(WFX,WF1) :- get_wait_flag(1.0,WFX,WF1). /* when this is ground you can do det. propagatons */
1086 get_wait_flag1(Info,WFX,WF1) :- %print(getwf1(Info)),nl, portray_waitflags(WFX),nl,
1087 get_wait_flag(1.0,Info,WFX,WF1).
1088
1089 get_binary_choice_wait_flag(_,no_wf_available,WF2) :- !, WF2=2.
1090 get_binary_choice_wait_flag(Info,WF,WF2) :- get_preference(data_validation_mode,true),!,
1091 get_binary_choice_wait_flag_exp_backoff(1048576,Info,WF,WF2).
1092 % in DV mode we do not want to enumerate bool(.) or similar; we want to wait for data enumeration to decide those
1093 % prio was 1048576, caused slowdown for TYPES_AUTORISES_RVF3_GEN__MRGA.mch before improvement to b_check_exists_wfwd
1094 get_binary_choice_wait_flag(Info,WFX,WF2) :- !, get_binary_choice_wait_flag_exp_backoff(4,Info,WFX,WF2).
1095 %get_binary_choice_wait_flag(Info,WFX,WF2) :- % use 3 rather than 2 to give priority over enumeration choicepoints
1096 % get_wait_flag(4,Info,WFX,WF2). /* when this is ground you can do binary propagatons */
1097
1098 %get_middle_wait_flag(Info,WFX,WFn) :- get_wait_flag(10,Info,WFX,WFn).
1099
1100 ?get_last_wait_flag(Info,WFX,Res) :- last_finite_priority(P), get_wait_flag(P,Info,WFX,Res).
1101
1102 % ------------------------
1103 get_wait_flag_infos(wfx(_,_,_,Infos),Infos).
1104 get_wait_flag_infos(no_wf_available,[]).
1105
1106 portray_wait_flag_infos(wfx(_,_,_,Infos)) :- !, portray_wait_flag_infos(Infos).
1107 portray_wait_flag_infos(no_wf_available) :- !.
1108 portray_wait_flag_infos(List) :- maplist(portray_wait_flag_info,List).
1109
1110 portray_wait_flag_info(call_stack(CallStack)) :- !, translate_call_stack(CallStack,TS), write(TS).
1111 portray_wait_flag_info(X) :- write(X),nl.
1112
1113
1114 :- use_module(library(ordsets),[ord_member/2,ord_add_element/3]).
1115 get_wait_flag_info(wfx(_,_,_,Infos),Info) :- member(Info,Infos).
1116
1117 % use if the Info field is known, requires ordered/sorted Infos list
1118 % used for wfx_no_enumeration information, see test 1368
1119 is_wait_flag_info(wfx(_,_,_,Infos),Info) :-
1120 (nonvar(Infos) -> ord_member(Info,Infos) ; print(var_waitflag),nl,fail).
1121
1122 %set_wait_flag_infos(wfx(WF0,Store,WFE,_),Infos,wfx(WF0,Store,WFE,Infos)).
1123 %set_wait_flag_infos(no_wf_available,_,no_wf_available).
1124
1125 % add new Info entry to WF store's info list
1126 add_wait_flag_info(wfx(WF0,Store,WFE,Infos),Info,wfx(WF0,Store,WFE,NewInfos)) :-
1127 (ord_add_element(Infos,Info,NewInfos) -> true %, print(added_wf_infos(NewInfos)),nl
1128 ; add_internal_error('Could not add_wait_flag_info:',(Info,Infos)),
1129 NewInfos = [Info|Infos]).
1130 add_wait_flag_info(no_wf_available,_,no_wf_available).
1131
1132
1133 % CALL STACK related predicates
1134 % --------------
1135 :- use_module(tools_lists,[ord_update_nonvar/4]).
1136 % push call stack information into the WF info field
1137 push_wait_flag_call_stack_info(no_wf_available,_,WF2) :- !, WF2=no_wf_available.
1138 push_wait_flag_call_stack_info(wfx(WF0,Store,WFE,Infos),CallInfo,wfx(WF0,Store,WFE,NewInfos)) :-
1139 ord_update_nonvar(call_stack(Stack),Infos,call_stack([CallInfo|Stack]),NewInfos),!,
1140 %print(push_call_stack(CallInfo,Stack)),nl,
1141 (var(Stack) -> Stack=[] % no call stack available yet
1142 ; true).
1143 push_wait_flag_call_stack_info(WF,CallInfo,WF) :- add_internal_error('Pushing call stack failed: ',CallInfo).
1144
1145 % only push in TRACE_INFO mode to avoid performance hit in regular mode
1146 opt_push_wait_flag_call_stack_info(WF,_,WF2) :- preference(provide_trace_information,false),!, WF2=WF.
1147 opt_push_wait_flag_call_stack_info(WF,CallInfo,WF2) :-
1148 push_wait_flag_call_stack_info(WF,CallInfo,WF2).
1149
1150 debug_opt_push_wait_flag_call_stack_info(WF,CallInfo,WF2) :-
1151 (debug_mode(off) -> WF2=WF
1152 ; opt_push_wait_flag_call_stack_info(WF,CallInfo,WF2)
1153 ).
1154
1155
1156 :- use_module(tools_lists,[ord_member_nonvar_chk/2]).
1157 add_call_stack_to_span(Span,wfx(_,_,_,Infos),NewSpan) :-
1158 (var(Infos) -> write(illegal_wfx),nl,fail ; true),
1159 ord_member_nonvar_chk(call_stack(CallStack),Infos),
1160 !,
1161 (Span = pos_context(Span1,call_stack(OldStack),Span2), % we already have a call_stack
1162 merge_call_stack(OldStack,CallStack,NewStack)
1163 -> NewSpan = pos_context(Span1,call_stack(NewStack),Span2)
1164 ; % we do not already have merged in a call-stack
1165 NewSpan = pos_context(Span,call_stack(CallStack),unknown)
1166 ).
1167 add_call_stack_to_span(Span,_,Span).
1168
1169 merge_call_stack(OldStack,AddStack,NewStack) :-
1170 reverse(OldStack,OR), OR=[FirstOld|_],
1171 reverse(AddStack,AR), AR=[FirstAdd|_],
1172 (FirstOld \= FirstAdd % we use \= and not \== as entries can contain different Prolog variables
1173 -> append(OldStack,AddStack,NewStack) % we have not yet incorporated the outer stack
1174 ; append(AR,_,OR) -> NewStack = OldStack % old stack incorporates new one; this is often (always?) the case
1175 ; NewStack = AddStack).
1176
1177 % a variation of add_error/4
1178 add_error_wf(Src,Msg,V,Span,WF) :-
1179 add_call_stack_to_span(Span,WF,NewSpan),
1180 add_error(Src,Msg,V,NewSpan).
1181
1182 add_internal_error_wf(Src,Msg,V,Span,WF) :-
1183 add_call_stack_to_span(Span,WF,NewSpan),
1184 add_internal_error(Src,Msg,V,NewSpan).
1185
1186 % a variation of add_error/4
1187 add_warning_wf(Src,Msg,V,Span,WF) :-
1188 add_call_stack_to_span(Span,WF,NewSpan),
1189 add_warning(Src,Msg,V,NewSpan).
1190
1191 % a variation of add_message/4
1192 add_message_wf(Src,Msg,V,Span,WF) :-
1193 add_call_stack_to_span(Span,WF,NewSpan),
1194 add_message(Src,Msg,V,NewSpan).
1195
1196 :- use_module(translate,[translate_call_stack/2]).
1197 portray_call_stack(Var) :- var(Var),!,
1198 add_internal_error('Illegal var WF:',portray_call_stack(Var)).
1199 portray_call_stack(wfx(_,_,_,Infos)) :-
1200 ord_member_nonvar_chk(call_stack(CallStack),Infos),!,
1201 translate_call_stack(CallStack,TS),!,write(TS),nl.
1202 portray_call_stack(_) :- write(no_call_stack_available),nl.
1203
1204 % indent showing short summary of call stack at left
1205 indent_call_stack(WF) :- get_call_stack(WF,CS),reverse(CS,RCS),
1206 indent_cs(RCS).
1207 indent_cs([]).
1208 indent_cs([H|T]) :- translate:render_call_short(H,Short),
1209 write(Short), write(' > '),
1210 indent_cs(T).
1211
1212 % copy call stack info from one wait flag to another
1213 copy_wait_flag_call_stack_info(wfx(_,_,_,FromInfos),wfx(WF0,Store,WFE,Infos),wfx(WF0,Store,WFE,NewInfos)) :-
1214 ord_member_nonvar_chk(call_stack(CallStack),FromInfos),!,
1215 ord_add_element(Infos,call_stack(CallStack),NewInfos).
1216 % Note: we do not copy expect_explicit_value (but we use this in b_trace_test_components only)
1217 copy_wait_flag_call_stack_info(_,WF,WF).
1218
1219 % get call stack, empty list if none available
1220 get_call_stack(Var,CS) :- var(Var),!,
1221 % some unit tests still perform calls with _WF waitflag store; TODO: rewrite the tests and activate this error msg
1222 % add_internal_error('Illegal var WF:',get_call_stack(Var,CS)),trace,
1223 CS=[].
1224 get_call_stack(wfx(_,_,_,FromInfos),Stack) :-
1225 ord_member_nonvar_chk(call_stack(CallStack),FromInfos),!,
1226 Stack=CallStack.
1227 get_call_stack(FromInfos,Stack) :-
1228 ord_member_nonvar_chk(call_stack(CallStack),FromInfos),!,
1229 Stack=CallStack.
1230 get_call_stack(_,[]).
1231
1232 :- use_module(library(lists),[select/3]).
1233 init_quantifier_wait_flag(OuterWF,_QK,Paras,ParaValues,Pos,WF) :-
1234 ? select(QK,Pos,Pos1),
1235 detect_quantifier_kind(QK,QuantKind),!, % see if we have position infos which indicate the origin of the comprehension_set / quantifier
1236 simplify_span(Pos1,SPos),
1237 init_wait_flags_and_push_call_stack(OuterWF,quantifier_call(QuantKind,Paras,ParaValues,SPos),WF).
1238 init_quantifier_wait_flag(OuterWF,QuantKind,Paras,ParaValues,Pos,WF) :-
1239 simplify_span(Pos,SPos),
1240 init_wait_flags_and_push_call_stack(OuterWF,quantifier_call(QuantKind,Paras,ParaValues,SPos),WF).
1241
1242 % check grounding of WFE flag occurs after everything else
1243 :- public observe_wait_flags_wfe/1. % debugging utility
1244 observe_wait_flags_wfe(wfx(WF0,WFX,WFE,Infos)) :- !, obs(WF0,WFX,WFE,Infos).
1245 observe_wait_flags_wfe(_).
1246 :- block obs(?,?,-,?).
1247 obs(WF0,WFX,WFE,Infos) :-
1248 add_message_wf(kernel_waitflags,'WFE grounded:',WFE,unknown,wfx(WF0,WFX,WFE,Infos)),
1249 var(WF0),!,
1250 add_error_wf(kernel_waitflags,'WF0 not grounded before WFE:',WF0,unknown,wfx(WF0,WFX,WFE,Infos)).
1251 obs(WF0,WFX,WFE,Infos) :- my_get_fdvars_att(WFX,FDList), FDList \= [], !,
1252 add_error_wf(kernel_waitflags,'FDVars not grounded before WFE:',FDList,unknown,wfx(WF0,WFX,WFE,Infos)).
1253 obs(WF0,WFX,WFE,Infos) :- my_get_wf_store_att(WFX,Heap), avl_to_list(Heap,List), List \= [], !,
1254 add_error_wf(kernel_waitflags,'Waitflags not grounded before WFE:',List,unknown,wfx(WF0,WFX,WFE,Infos)).
1255 obs(_WF0,_WFX,_WFE,_Infos).
1256
1257 % a version of opt_push_wait_flag_call_stack_info for quantifiers
1258 opt_push_wait_flag_call_stack_quantifier_info(WF,_,_,_,_,WF2) :-
1259 preference(provide_trace_information,false),!, WF2=WF.
1260 opt_push_wait_flag_call_stack_quantifier_info(no_wf_available,_,_,_,_,WF2) :- !, WF2=no_wf_available.
1261 opt_push_wait_flag_call_stack_quantifier_info(WF,QuantKind,Paras,ParaValues,Pos,WF2) :-
1262 simplify_span(Pos,SPos),
1263 opt_push_wait_flag_call_stack_info(WF,quantifier_call(QuantKind,Paras,ParaValues,SPos),WF2).
1264
1265 detect_quantifier_kind(prob_annotation('LAMBDA'),lambda).
1266 detect_quantifier_kind(quantifier_kind(QK),QK).
1267
1268 %init_wait_flags_and_push_call_stack(_,_,WF) :- !, init_wait_flags(WF). % comment in to disable tracking quantifiers
1269 % makes a difference for memoize test 1968 with recursive calls
1270 init_wait_flags_and_push_call_stack(OuterWF,CallStackTerm,WF) :-
1271 init_wait_flags_and_push_call_stack(OuterWF,CallStackTerm,[],WF).
1272 init_wait_flags_and_push_call_stack(OuterWF,CallStackTerm,OtherWFInfos,WF) :-
1273 get_call_stack(OuterWF,OuterCallStack),
1274 %print(init_quantifier(QuantKind,Paras,OuterCallStack)),nl,
1275 CallStack = [CallStackTerm|OuterCallStack],
1276 get_wf_all_dynamic_infos(OuterWF,DynInfos),
1277 copy_new_important_wf_infos(OuterWF,OtherWFInfos,NewWFInfos),
1278 init_wait_flags_with_sd_infos(WF,[call_stack(CallStack)|NewWFInfos],DynInfos).
1279
1280 init_wait_flags_with_call_stack(WF,CallStack) :-
1281 init_wait_flags_with_infos(WF,[call_stack(CallStack)]).
1282
1283 % the following is optimised for the fact that expect_explicit_value is the only important info
1284 % other than call_stack which is dealt with separately:
1285 copy_new_important_wf_infos(OuterWF,OtherWFInfos,NewWFInfos) :-
1286 (get_wait_flag_infos(OuterWF,OuterWFInfos),
1287 member(expect_explicit_value,OuterWFInfos),
1288 nonmember(expect_explicit_value,OtherWFInfos)
1289 -> NewWFInfos = [expect_explicit_value|OtherWFInfos]
1290 ; NewWFInfos = OtherWFInfos).
1291
1292 % :- use_module(library(lists),[include/3]).
1293 % get important static WF infos other than call-stack
1294 % call stack dealt with separately
1295 %get_important_wf_infos(WF,ImportantInfos) :-
1296 % get_wait_flag_infos(WF,Infos),
1297 % include(is_important,Infos,ImportantInfos).
1298 %is_important(expect_explicit_value).
1299
1300 % --------------
1301
1302 :- if((environ(prob_safe_mode,true) ; environ(prob_src_profile,true) ; environ(prob_profile,true))).
1303 % TODO: should this be controlled also by prob_profiling_on preference
1304 add_debug_wait_flag_info(Infos,Info,NewInfos) :- ord_add_element(Infos,Info,NewInfos).
1305 init_debug_wait_flag_info([],Infos) :- !, Infos=[].
1306 init_debug_wait_flag_info([H|T],Infos) :- !, Infos=[H|T].
1307 init_debug_wait_flag_info(Info,[Info]).
1308 :- else.
1309 add_debug_wait_flag_info(Infos,_,Infos).
1310 init_debug_wait_flag_info(_,[]).
1311 :- endif.
1312 % ------------------------
1313
1314 % BINARY CHOICE WAITFLAGS
1315
1316 :- use_module(tools_platform, [max_tagged_integer/1, max_tagged_pow2/1]).
1317 % last power of 2 exponent that is still a finite priority
1318 last_finite_pow2_prio_exponent(Lim) :- max_tagged_pow2(Pow),
1319 Lim is Pow-1. % stay below last_finite_priority, i.e., max_tagged_integer-1024
1320
1321 % utility to get power of two priority:
1322 get_pow2_binary_choice_priority(Exp,Prio) :- last_finite_pow2_prio_exponent(Lim),
1323 (Exp =< Lim -> Prio is floor(2**Exp)
1324 ; Prio is floor(2**Lim)).
1325
1326 % get a binary choice wait flag, start with 2 but for every further call double the priority (exponential backoff)
1327 % idea: give priority to data enumerations when too many choice points arise
1328 % TO DO: when triggering: keep the WF info with an empty trigger or store in separate info field
1329 get_binary_choice_wait_flag_exp_backoff(Info,WFX,WF2) :-
1330 get_binary_choice_wait_flag_exp_backoff(2,Info,WFX,WF2).
1331 get_binary_choice_wait_flag_exp_backoff(_,_Info,no_wf_available,WF2) :- !, WF2=2.
1332
1333 % StartPrio should normally be a power of 2:
1334 get_binary_choice_wait_flag_exp_backoff(MinPrio,Info,wfx(WF0,Store,WFE,_II),WF2) :-
1335 (nonvar(WFE) -> add_message(kernel_waitflags,'Getting waitflag from grounded store: ',Info),
1336 % probably a good idea to use clone_wait_flags_from1 if this happens
1337 WF2=WF0 % use at least WF0 for WF2, maybe it is still being enumerated
1338 ; my_get_wf_store_att(Store,Heap),
1339 large_finite_priority(Lim), % avoid creating too big numbers and stay in finite area
1340 (MinPrio < 1 -> StartPrio=1 ; StartPrio=MinPrio),
1341 get_bin_aux(StartPrio,Lim,Info,Heap,NewHeap,WF2),
1342 put_mutable_wf_store_attr(Store,NewHeap)
1343 ).
1344
1345 % to do : maybe store attribute of current exponential ??
1346 get_bin_aux(Prio,Lim,Info,Heap,NewHeap,WF2) :-
1347 avl_fetch(Prio,Heap,wf(WFs,OldInfo)),
1348 !,
1349 (%Prio >= Lim -> pop_waitflag(WFs,WF2,_), % should we get last waitflag or simply push
1350 % NewHeap=Heap ; /* for large finite priorities single waitflag stored */
1351 Prio<Lim, OldInfo = '$binary'(_)
1352 -> double_prio(Prio,P2), get_bin_aux(P2,Lim,Info,Heap,NewHeap,WF2)
1353 ; push_waitflag(WFs,WF2,WFs2),
1354 avl_store(Prio,Heap,wf(WFs2,'$binary'(Info)),NewHeap) % just update Info; so that next time we double
1355 ).
1356 get_bin_aux(Prio,_,Info,Heap,NewHeap,WF2) :- % we have found an unused power of 2
1357 avl_store(Prio,Heap,wf(WF2,'$binary'(Info)),NewHeap).
1358
1359 % ------------------------
1360
1361 get_enumeration_finished_wait_flag(wfx(_,_,WFE,_),Res) :- !,Res=WFE.
1362 get_enumeration_finished_wait_flag(no_wf_available,WFE) :- !,WFE=1.
1363 get_enumeration_finished_wait_flag(W,E) :-
1364 add_internal_error('Waitflags in wrong format: ',get_enumeration_finished_wait_flag(W,E)).
1365
1366
1367 ground_det_wait_flag(wfx(WF0,Store,_,WFInfos)) :- !, %% print(ground_det_wait_flag),nl,
1368 ? grd_wf0(WF0),
1369 deref_store(Store,DStore),
1370 init_wf_infos_for_grounding(WFInfos,WFInfos2),
1371 ground_waitflags_store_up_to_no_clpfd(1,DStore,WFInfos2). % no CLPFD labeling required for prio until 1
1372 ground_det_wait_flag(no_wf_available) :- !.
1373 ground_det_wait_flag(W) :-
1374 add_internal_error('Waitflags in wrong format: ',ground_det_wait_flag(W)).
1375
1376 ground_wait_flag_to(wfx(WF0,Store,_,WFInfos),Limit) :- !,%% print(ground_det_wait_flag),nl,
1377 grd_wf0(WF0),
1378 deref_store(Store,DStore),
1379 init_wf_infos_for_grounding(WFInfos,WFInfos2),
1380 ? ground_waitflags_store_clpfd_up_to(Limit,DStore,WFInfos2).
1381 ground_wait_flag_to(no_wf_available,_) :- !.
1382 ground_wait_flag_to(W,Limit) :-
1383 add_internal_error('Waitflags in wrong format: ',ground_wait_flag_to(W,Limit)).
1384
1385 useless_wait_flag(treat_next_integer(_VarID,Val,Rest)) :- Rest==[],
1386 kernel_tools:ground_value(Val).
1387 useless_wait_flag(tighter_enum(_VarID,Val,_Type)) :- %ground(Val).
1388 kernel_tools:ground_value(Val).
1389 %nonvar(Val), quick_ground(Val).
1390 %quick_ground(int(N)) :- integer(N).
1391 %quick_ground(string(S)) :- atom(S).
1392
1393 % show which waitflags are actually useless
1394 % TODO: think about pruning the WF store; e.g., when calling deref_store ??
1395 :- public portray_useless_waitflags_in_store/1.
1396 portray_useless_waitflags_in_store(Store) :-
1397 my_get_wf_store_att(Store,Heap),
1398 avl_member(AMin,Heap,wf(_,Info)),
1399 useless_wait_flag(Info),
1400 format(' useless waitflag ~w --> ~w~n',[AMin,Info]),
1401 fail.
1402 portray_useless_waitflags_in_store(_).
1403
1404
1405
1406 ground_waitflags_store_up_to_no_clpfd(Prio,Store,WFInfos) :- % print(prio(Prio)),nl,
1407 my_get_wf_store_att(Store,Heap), % portray_waitflags_store(Store), %
1408 ( avl_min(Heap,Min,_) -> % heap is non-empty, first element is WF with priority Min
1409 ( Min =< Prio -> % we have non-ground waitflags with a matching priority
1410 (avl_del_min_waitflag(Heap,AMin,wf(WFMin,Info),NewHeap),AMin==Min % remove the first element
1411 -> true
1412 ; add_internal_error('Could not remove min. in ground_waitflags_store_up_to_no_clpfd: ',Min:Info:Heap), fail
1413 ),
1414 put_mutable_wf_store_attr(Store,NewHeap), % write the modified heap back
1415 %% print(grounding2(Prio,_Info)),nl, %%
1416 ground_prio(Min,WFMin,Info,WFInfos),
1417 ground_waitflags_store_up_to_no_clpfd(Prio,Store,WFInfos) % continue recursively
1418 ; % no more matching waitflags present
1419 true) % stop here
1420 ; % heap is empty, nothing more to do
1421 true). % stop here
1422
1423 ground_waitflags_store_clpfd_up_to(Limit,Store,WFInfos) :-
1424 my_get_wf_store_fdvars_atts(Store,Heap,FDVars),
1425 !,
1426 ( avl_del_min_waitflag(Heap,Prio,wf(WFMin,Info),NewHeap),
1427 Prio =< Limit
1428 -> % we have non-ground waitflags with a matching priority
1429 ( my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS),
1430 fd_priority_leq_limit(NextFDVar,FDPrio,Limit)
1431 -> (FDPrio > Limit -> true
1432 ? ; enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS,WFInfos))
1433 ; put_mutable_wf_store_attr(Store,NewHeap), % write the modified heap back
1434 ? ground_prio(Prio,WFMin,Info,WFInfos), % ground waitflag
1435 ? ground_waitflags_store_clpfd_up_to(Limit,Store,WFInfos) % continue recursively
1436 )
1437 ; % no pending waitflag
1438 my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS),
1439 fd_priority_leq_limit(NextFDVar,FDPrio,Limit)
1440 -> enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS,WFInfos)
1441 ; true % heap and FDVars list is empty, nothing more to do
1442 ).
1443 ground_waitflags_store_clpfd_up_to(Limit,Store,WFInfos) :-
1444 add_internal_error('Illegal Waitflag: ',ground_waitflags_store_clpfd_up_to(Limit,Store,WFInfos)),fail.
1445
1446 % --------------------
1447
1448 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF),
1449 kernel_waitflags:get_wait_flag(10001,test1,WF,WF1),
1450 kernel_waitflags:get_wait_flag(10001,test2,WF,WF2),
1451 check_eq(WF,wfx(_,Store,_,_)),
1452 kernel_waitflags:my_get_wf_store_fdvars_atts(Store,Heap,_FDVars),
1453 kernel_waitflags:avl_del_min_waitflag(Heap,Prio1,wf(WFMin1,_Info1),Heap2),
1454 check_eqeq(Prio1,10001), check_eqeq(WFMin1,WF1),
1455 kernel_waitflags:avl_del_min_waitflag(Heap2,Prio2,wf(WFMin2,_Info2),_),
1456 check_eqeq(Prio2,10001), check_eqeq(WFMin2,WF2)
1457 )).
1458
1459 % a conditional version of avl_del_min: where we either delete or update the minimal element
1460
1461 :- if(current_prolog_flag(dialect, swi)).
1462 avl_del_min_waitflag(AVL0, Key, wf(NextWF,Info), AVL) :-
1463 avl_del_min(AVL0,Key,Val0,AVL1),
1464 Val0 = wf(WFMin,Info),
1465 pop_waitflag(WFMin,NextWF,RemainingWFMin),
1466 (RemainingWFMin==[] -> AVL=AVL1
1467 ; avl_store(Key,AVL1,wf(RemainingWFMin,Info),AVL)
1468 ).
1469
1470 :- else. % swi
1471 % optimized code to delete and update in one traversal; requires access to inner predicates of library(avl)
1472
1473 avl_del_min_waitflag(AVL0, Key, Val, AVL) :-
1474 avl_del_min_wf_aux(AVL0, Key, Val, AVL, _).
1475
1476 avl_del_min_wf_aux(node(K,V,B,L,R), Key, Val, AVL, Delta) :-
1477 ( L == empty ->
1478 Key = K,
1479 V = wf(WFMin,Info),
1480 pop_waitflag(WFMin,NextWF,RemainingWFMin),
1481 Val = wf(NextWF,Info),
1482 (RemainingWFMin==[] -> % single waitflag
1483 %print(single_wf(Key,WFMin,Info)),nl,
1484 AVL = R, Delta=1 % we delete the minimal element
1485 ; % there are multiple waitflags for the same priority
1486 % print(multiple_wf(Key,WFMin,Info)),nl,
1487 AVL = node(K,wf(RemainingWFMin,Info),B,L,R), % update AVL with remaining waitflags
1488 Delta = 0 % we do not delete the node, but update it
1489 )
1490 ; avl_del_min_wf_aux(L, Key, Val, L1, D1),
1491 B1 is B+D1,
1492 avl:avl(B1, K, V, L1, R, AVL),
1493 avl:avl_shrinkage(AVL, D1, Delta)
1494 ).
1495
1496 :- endif. % else - swi
1497
1498 % -------------------
1499
1500 update_waitflag(Prio,CurrentWaitflag,NewWaitFlag,WF) :-
1501 /* if the CurrentWaitflag is already ground and now a new WF with lower priority has been added to the store:
1502 create a new non-ground waitflag to give priority to new WF with lower priority value */
1503 (var(CurrentWaitflag) -> NewWaitFlag=CurrentWaitflag
1504 ; WF=wfx(_WF0,Store,_,_),my_get_wf_store_att(Store,Heap),
1505 (avl_min(Heap,Min,wf(_NewerWF,_Info)),
1506 (is_inf_or_overflow_card(Prio) ; Min<Prio)
1507 -> get_waitflag_from_store(Prio,updated_wf,Store,NewWaitFlag) % could be optimized
1508 % ,print(updating_wf(Prio,CurrentWaitflag,NewWaitFlag,Min)),nl
1509 ; fdvar_with_higher_prio_exists(Store,Prio) -> get_waitflag_from_store(Prio,updated_wf,Store,NewWaitFlag)
1510 ; NewWaitFlag=CurrentWaitflag
1511 )
1512 ).
1513
1514 fdvar_with_higher_prio_exists(Store,Limit) :-
1515 my_get_fdvars_att(Store,FDVars),
1516 my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,_),
1517 fd_priority_leq_limit(NextFDVar,_,Limit).
1518
1519 % get the minimum waitflag, if it exists
1520 get_minimum_waitflag_prio(wfx(WF0,Store,_,_),MinPrio,Info) :-
1521 (var(WF0) -> MinPrio=0, Info=wf0
1522 ; WF0=s(WF00), var(WF00) -> MinPrio = 1, Info=wf0_nonvar
1523 ; my_get_wf_store_att(Store,Heap),
1524 avl_min(Heap,MinPrio,wf(_LWF,Info))).
1525
1526
1527 add_wd_error(Msg,Term,WF) :-
1528 try_extract_span(Term,Span), % try extract span if possible;
1529 % e.g., for {x|x>10 } = res & card(res)=10 we have a closure/3 as Term
1530 add_wd_error_span(Msg,Term,Span,WF).
1531
1532 try_extract_span(closure(_,_,B),Span) :- !, Span=B.
1533 try_extract_span(b(_,_,Infos),Span) :- !, Span=Infos.
1534 try_extract_span(_,unknown).
1535
1536 add_wd_error_set_result(Msg,Term,Result,ResultValue,Span,WF) :-
1537 is_wd_guarded_result(Result), % just dummy co-routine to detect variables which have WD assignments pending on them; should not be enumerated
1538 % add well-definedness error but also set Result Variable to ResultValue to trigger pending co-routines if wd_guarded
1539 add_abort_error7(well_definedness_error,Msg,Term,Result,ResultValue,Span,WF).
1540
1541 add_wd_error_span(Msg,Term,Span,WF) :-
1542 add_abort_error_span(well_definedness_error,Msg,Term,Span,WF).
1543 % the same but adding a WD error directly, without any delay:
1544 add_wd_error_span_now(Msg,Term,Span,WF) :-
1545 add_abort_error2(true,well_definedness_error,Msg,Term,0,0,Span,WF).
1546
1547 % mark a variable as being wd_guarded
1548 % TODO: use put_wf_abort_pending(X).
1549 :- block is_wd_guarded_result(-).
1550 is_wd_guarded_result(string(X)) :- !, is_wd_guarded_result(X).
1551 is_wd_guarded_result(int(X)) :- !, is_wd_guarded_result(X).
1552 is_wd_guarded_result(fd(X,_)) :- !, is_wd_guarded_result(X).
1553 is_wd_guarded_result(_).
1554
1555 add_abort_error_span(ErrType,Msg,Term,Span,WF) :- add_abort_error7(ErrType,Msg,Term,0,0,Span,WF).
1556
1557
1558 :- use_module(probsrc(debug), [debug_mode/1]).
1559 add_abort_error7(_ErrType,_Msg,_Term,_Result,_ResultValue,_Span,_WF) :-
1560 preference(disprover_mode,true), % We could also add info in the WF that WD errors should be ignored
1561 !, % When Disproving we can assume well-definedness of the PO; sometimes the relevant hypotheses will be filtered out
1562 fail.
1563 add_abort_error7(ErrType,Msg,Term,Result,ResultValue,Span,WF) :-
1564 preference(raise_abort_immediately,F), F \= false,
1565 !,
1566 (F=full -> AWF=1 % really raise immediately; may entail more spurious messages, particularly in WF0
1567 ; get_idle_wait_flag(add_abort_error7,WF,AWF), %get_wait_flag0(WF,AWF),
1568 (var(AWF),debug_mode(on) ->
1569 (pending_abort_error(WF)
1570 -> format(user_output,'Additional WD Error pending:~n',[]) % TODO: no use in calling add_abort_error2 below
1571 ; true),
1572 add_message(wd_error_pending,Msg,Term,Span)
1573 ; true),
1574 mark_pending_abort_error(WF,Msg,Term,Span)
1575 ),
1576 %when(nonvar(AWF),
1577
1578 %(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)),
1579 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),
1580 add_abort_error2(AWF,ErrType,NewMsg,Term,Result,ResultValue,Span,WF).
1581 add_abort_error7(ErrType,Msg,Term,Result,ResultValue,Span,WF) :-
1582 %add_message_wf(add_abort_error,'Possible WD Error: ',Term,Span,WF), % happens a lot in test 1886
1583 mark_pending_abort_error(WF),
1584 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:
1585 %integer_priority(P), get_wait_flag(P,add_abort_error7,WF,AWF), % causes problem for test 1122
1586 add_abort_error2(AWF,ErrType,Msg,Term,Result,ResultValue,Span,WF).
1587
1588
1589 :- use_module(error_manager,[add_new_event_in_error_scope/1]).
1590 :- use_module(state_space,
1591 [store_abort_error_for_context_state_if_possible/4]).
1592 :- block add_abort_error2(-,?,?,?,?,?,?,?).
1593 add_abort_error2(wd_guarded,_Err,_Msg,_Term,Result,ResultValue,_Span,_WF) :- !,% ignoring error message; no-wd problem
1594 % set Result to default ResultValue; useful to get rid of pending co-routines; result will not be used
1595 (Result=ResultValue -> true ; print(add_abort_error_failure(Result,ResultValue)),nl).
1596 add_abort_error2(_AWF,ErrType,Msg,Term,_,_,Span,WF) :-
1597 add_call_stack_to_span(Span,WF,Span2),
1598 (register_abort_errors_in_error_scope(Level)
1599 -> (get_preference(provide_trace_information,false)
1600 -> format('Registering WD Error in error scope (~w): ~w~n',[Level,Msg])
1601 ; add_message_wf(wd_error,'Registering WD Error in error scope: ',Msg,Span,WF)
1602 ),
1603 assert(pending_inner_abort_error(Level,ErrType,Msg,Term,Span2))
1604 % ,trace,write(level(Level,ErrType)),nl,get_enumeration_finished_wait_flag(WF,WFE), when(nonvar(WFE),(write(abewf(Level,Msg)),nl,trace))
1605 % add_new_event_in_error_scope(abort_error(ErrType)) % do not register as error yet; we will copy it to the outer WF
1606 ; store_abort_error_for_context_state_if_possible(ErrType,Msg,Term,Span2)
1607 -> true
1608 ; add_error(add_abort_error2,'Could not store error: ',(Msg,Term),Span2)),
1609 % 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
1610 fail.
1611
1612 :- dynamic register_abort_errors_in_error_scope/0, pending_inner_abort_error/5.
1613
1614
1615 :- use_module(eventhandling,[register_event_listener/3]).
1616 :- register_event_listener(clear_specification,reset_waitflags, 'Reset kernel_waitflags.').
1617
1618 reset_waitflags :-
1619 retractall(register_abort_errors_in_error_scope),
1620 retractall(pending_inner_abort_error(_,_,_,_,_)),
1621 bb_put(wf_findall_nesting_level,0).
1622
1623 % clear pending inner abort errors
1624 % there is still a potential issue if a time-out appears just in the middle
1625 start_attach_inner_abort_errors(NewLevel,_PP) :-
1626 (bb_get(wf_findall_nesting_level,L) -> Level=L ; Level=0),
1627 NewLevel is Level+1,
1628 bb_put(wf_findall_nesting_level,NewLevel),
1629 %write(enter(NewLevel,PP)),nl,
1630 retractall(pending_inner_abort_error(NewLevel,_,_,_,_)).
1631
1632 % succeeds when abort errors should be asserted for later copying (at the end of a findall) to the outer WF
1633 register_abort_errors_in_error_scope(Level) :-
1634 register_abort_errors_in_error_scope,
1635 get_wf_findall_nesting_level(Level).
1636
1637 get_wf_findall_nesting_level(Level) :-
1638 (bb_get(wf_findall_nesting_level,L) -> Level=L
1639 ; write(missing_wf_findall_nesting_level),nl, Level=0).
1640
1641 % extract all pending inner abort errors and attach them to the outer WF store
1642 % should be called after ground_inner_wait_flags_in_context
1643 re_attach_pending_inner_abort_errors(Level,OuterWF,SomePending) :-
1644 get_wf_findall_nesting_level(L),
1645 %write(exit(L)),nl,
1646 (Level=L -> true
1647 ; write(unexpected_wf_findall_nesting(L,expected(Level))),nl
1648 % could happen if a time-out occured in middle of updating wf_findall_nesting_level info
1649 ),
1650 LL1 is Level-1, bb_put(wf_findall_nesting_level,LL1),
1651 findall(pending(ErrType,Msg,Term,Span),
1652 retract(pending_inner_abort_error(Level,ErrType,Msg,Term,Span)), PendingList),
1653 (PendingList=[]
1654 -> SomePending=none_pending
1655 ; SomePending=pending_abort_errors,
1656 % the next is useless when we call re_attach_pending_abort_errors in call_cleanup when the call failed
1657 maplist(re_add_pending_aux(OuterWF,LL1),PendingList)
1658 ).
1659
1660 re_add_pending_aux(WF,OuterLevel,pending(ErrType,Msg,Term,Span)) :-
1661 (get_preference(provide_trace_information,true), debug_mode(on)
1662 -> add_message_wf(wd_error,'Reattaching WD Error to outer WF: ',Msg,Span,WF)
1663 ; format('Reattaching error ~w in outer WF (~w)~n',[ErrType,OuterLevel])
1664 ),
1665 add_abort_error_span(ErrType,Msg,Term,Span,WF).
1666
1667 :- public portray_pending/0.
1668 portray_pending :- pending_inner_abort_error(_Level,ErrType,Msg,Term,Span),
1669 add_message(ErrType,Msg,Term,Span),fail.
1670 portray_pending.
1671
1672 % add abort / state error directly (now), but do not fail unlike add_wd_error_span_now
1673 add_state_error_wf(ErrType,Msg,Term,Span,WF) :-
1674 add_call_stack_to_span(Span,WF,Span2),
1675 (store_abort_error_for_context_state_if_possible(ErrType,Msg,Term,Span2) -> true
1676 ; add_error_wf(ErrType,Msg,Term,Span2,WF)
1677 ).
1678
1679
1680 % ground enumeration finished waitflag unless abort is pending
1681 % useful inside of exists, ... to complete all pending co-routines
1682 ground_ef_wait_flag_unless_abort_pending(wfx(_WF0,_WFX,WFE,_)) :-
1683 var(WFE), %print(check_abort_pending(_WF0,_WFX,WFE)),nl,
1684 is_not_wf_abort_pending(WFE),
1685 !, %print(grd_ef(WFE)),nl,
1686 grd_ef(WFE).
1687 ground_ef_wait_flag_unless_abort_pending(_).
1688
1689 % special grounding of inner waitflags taking context (all_solutions, positive, negative) into account
1690 % one should call re_attach_pending_inner_abort_errors on the OuterWF after the forall/negation
1691 % in order to retract pending_inner_abort_error and re-attach the WD errors to the OuterWF
1692 ground_inner_wait_flags_in_context(all_solutions,WF) :-
1693 WF = wfx(_WF0,_WFX,WFE,_),!,
1694 ? ground_constraintprop_wait_flags(WF),
1695 (var(WFE),
1696 is_wf_abort_pending(WFE)
1697 -> !, % cut, we have found a WD error, whole findall result is useless anyway; cut
1698 % happens in tests 629, 1921, 1966, 2224
1699 debug_format(19,'WD Error(s) pending, could be spurious ~n',[]),
1700 assert(register_abort_errors_in_error_scope),
1701 call_cleanup(grd_ef(WFE), % will not raise WD errors but store them for OuterWF
1702 retract(register_abort_errors_in_error_scope))
1703 ; grd_ef(WFE)
1704 ).
1705 ground_inner_wait_flags_in_context(negative,WF) :- !,
1706 ? ground_inner_wait_flags_in_context(all_solutions,WF).
1707 ground_inner_wait_flags_in_context(_,WF) :-
1708 ground_wait_flags(WF). % TODO: do not ground WFE, ensure it is shared;
1709 % but currently positive context only used in external funs with wf_not_available
1710
1711
1712 % try and get a user-readable description of the context in which the WF store was created
1713 get_wait_flags_context_msg(wfx(_WF0,_WFX,_WFE,Infos),Msg) :-
1714 member(Info,Infos),
1715 get_info_context_description(Info,Msg).
1716
1717 :- use_module(bsyntaxtree, [get_texpr_ids/2]).
1718 get_info_context_description(call_stack(CS),Msg) :- translate_call_stack(CS,Msg).
1719 % TODO: do we need expansion_context anymore?
1720 get_info_context_description(expansion_context(Type,Parameters),Msg) :-
1721 (Parameters=[b(identifier(_),_,_)|_]
1722 -> get_texpr_ids(Parameters,Ids),
1723 get_msg_aux(Type,Ids,Parameters,Msg)
1724 ; get_msg_aux(Type,Parameters,[],Msg)
1725 ).
1726 get_info_context_description(expansion_context_with_pos(Type,Parameters,Info),Msg) :-
1727 get_msg_aux(Type,Parameters,Info,Msg).
1728 % also deal with : check_element_of_function_closure_nowf(MemoID)
1729
1730 get_msg_aux(Type,Parameters,Info,Msg) :-
1731 extract_span_description(Info,PosMsg),!, % we could do this only if preference trace_info set ?
1732 ajoin_with_sep(['expanding', Type, 'at', PosMsg, 'over ids'|Parameters],' ',Msg).
1733 get_msg_aux(Type,[],_Info,Msg) :- !,
1734 ajoin(['expanding ', Type],Msg).
1735 get_msg_aux(Type,Parameters,_Info,Msg) :-
1736 ajoin_with_sep(['expanding', Type, 'over ids'|Parameters],' ',Msg).
1737
1738
1739 % check whether an abort error is pending.
1740 pending_abort_error(wfx(_WF0,_WFX,WFE,_)) :- var(WFE),
1741 is_wf_abort_pending(WFE). %print(got_abort_pending),nl.
1742 % succeed once for every pending abort error in Waitflag store
1743 pending_abort_error(wfx(_WF0,_WFX,WFE,_),Msg,Term,Span) :- var(WFE),
1744 is_wf_abort_pending(WFE),
1745 frozen(WFE,Goal), %print(goal(Goal)),nl,
1746 pending_abort_error_aux(Goal,Msg,Term,Span).
1747
1748 pending_abort_error_aux(kernel_waitflags:add_abort_error2(_,_ErrType,Msg,Term,_Result,_ResultValue,Span,_), Msg,Term,Span).
1749 pending_abort_error_aux(add_abort_error2(_,_ErrType,Msg,Term,_Result,_ResultValue,Span,_), Msg,Term,Span).
1750 pending_abort_error_aux(kernel_waitflags:mark_pending_abort_error2(_,Msg,Term,Span), Msg,Term,Span).
1751 pending_abort_error_aux((A,B),Msg,Term,Span) :-
1752 (pending_abort_error_aux(A,Msg,Term,Span) ;
1753 pending_abort_error_aux(B,Msg,Term,Span) ).
1754
1755 % explicitly mark that an abort error is pending
1756 mark_pending_abort_error(wfx(_WF0,_WFX,WFE,_)) :- !, % print(mark_abort_pending(_WFX)),nl,
1757 (var(WFE) -> put_wf_abort_pending(WFE)
1758 ; true).
1759 mark_pending_abort_error(no_wf_available).
1760
1761 % explicitly mark that an abort error is pending with storing information
1762 mark_pending_abort_error(wfx(_WF0,_WFX,WFE,_),Msg,Term,Span) :-
1763 (var(WFE) -> put_wf_abort_pending(WFE),
1764 mark_pending_abort_error2(WFE,Msg,Term,Span)
1765 ; true).
1766 mark_pending_abort_error(no_wf_available,_Msg,_Term,_Span).
1767
1768 :- block mark_pending_abort_error2(-,?,?,?).
1769 mark_pending_abort_error2(_,_,_,_).
1770 % ---------------------------------------------
1771
1772
1773 get_large_finite_wait_flag(Info,WFX,WF2) :-
1774 large_finite_priority(P),
1775 get_wait_flag(P,Info,WFX,WF2).
1776
1777 get_enumeration_starting_wait_flag(Info,WFX,WF2) :-
1778 last_finite_priority(P),
1779 get_wait_flag(P,enumeration_starting(Info),WFX,WF2).
1780 %get_enumeration_almost_finished_wait_flag(Info,WFX,WF2) :-
1781 % max_tagged_integer(P), % largest tagged value
1782 % get_wait_flag(P,enumeration_starting(Info),WFX,WF2).
1783
1784
1785 get_integer_enumeration_wait_flag(Info,WFX,WF2) :-
1786 integer_priority(Prio),
1787 get_wait_flag(Prio,Info,WFX,WF2).
1788
1789 % A few hardcoded priorities
1790 large_finite_priority(P) :- integer_priority(I), P is I-1000.
1791 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
1792 %integer_priority(10000000). % old value
1793 integer_priority(Prio) :- % priority for X : INTEGER enumerations
1794 max_tagged_integer(P),
1795 Prio is P - 1024.
1796 % note inf_type_prio sets some priorities such as seq(INTEGER) ... higher
1797
1798 % priority for sets of infinite type (POW(INTEGER)...)
1799 integer_pow2_priority(Prio) :- % was 10000010
1800 integer_priority(P), Prio is P+10.
1801
1802 last_priority(P) :- % was 10000010
1803 max_tagged_integer(P).
1804
1805 % ensure that we do not exceed priority of type enumeration or use too big numbers
1806 get_bounded_wait_flag(Prio,Info,WF,LWF) :-
1807 get_bounded_priority(Prio,P),
1808 get_wait_flag(P,Info,WF,LWF).
1809
1810 get_bounded_priority(Prio,P) :- number(Prio),
1811 Prio>268435452, % 268435455 is max_tagged_integer on 32 bit platforms
1812 last_finite_priority(MAX),
1813 Prio>MAX,
1814 !, P=MAX.
1815 get_bounded_priority(P,P).
1816
1817 % -------------
1818
1819 % copy waitflags from one waitflag store to another; warning: can ground WF0 from outer store
1820 % the assumption is that the outer store will be grounded/enumerated
1821 % the inner store 1 will now point to the outer store 2 in case some co-routines still add waitflags
1822 copy_waitflag_store(wfx(WF0,Store1,WFE1,_),wfx(WF0,Store2,WFE2,_Info2)) :-
1823 %print(copy_wf(WF0,Store1,Store2,WFE1,WFE2)),nl,
1824 my_get_wf_store_fdvars_atts(Store1,Heap,FDList1),
1825 (FDList1 = [] -> true
1826 ; my_get_fdvars_att(Store2,FDList2),
1827 %append(FDList1,FDList2,NewFDList), % used to be like this
1828 append_wf(FDList2,FDList1,NewFDList), % now we add inner FD variables to end
1829 put_mutable_wf_fdvars_attr(Store2,NewFDList)
1830 ),
1831 avl_to_list(Heap,WF_List),
1832 %print(copying(FDList1,WF_List)),nl,
1833 my_get_wf_store_att(Store2,Heap2),
1834 add_wf(WF_List,Heap2,NewHeap2),
1835 put_mutable_wf_store_attr(Store2,NewHeap2),
1836 set_mutable_ref(Store1,Store2),
1837 % ensures that calls which add waitflags now get-redirected to waitflag store 2
1838 % get_ef from Store2 and add trigger to enumerate Store1?
1839 %portray_waitflags(wfx(WF0,Store2,WFE2,Info2)),
1840 WFE1=WFE2.
1841
1842 append_wf([],FDL,FDL).
1843 append_wf([H|T],FDL2,Res) :-
1844 (ground(H) -> append_wf(T,FDL2,Res) ; Res=[H|RT], append_wf(T,FDL2,RT)).
1845
1846 % Note: add_wf will construct multiple WF variables also for finite_priorities,
1847 % get_waitflag_from_store will only keep multiple entries for infinite ones
1848 add_wf([],Heap,Heap).
1849 add_wf([Prio-wf(WF1,Info)|T],Heap,NewHeap) :-
1850 % purpose: copy waitflag WFlag to WF store; if same priority exists it will be appended at end
1851 P=Prio,
1852 (avl_fetch(P,Heap,wf(WFs,_OldInfo))
1853 -> % priority waitflag(s) exist; add inner store WF1 at end
1854 merge_waitflag_queues(WF1,WFs,MergedWFs), % TO DO: just unify WF 1 and 0.9 or smaller ?
1855 avl_store(P,Heap,wf(MergedWFs,Info),Heap2)
1856 ; % this priority does not yet exist in outer store
1857 avl_store(P,Heap,wf(WF1,Info),Heap2)
1858 ),
1859 add_wf(T,Heap2,NewHeap).
1860
1861
1862 % try and find an exact match for a waitflag info; this is linear in size of waitflag store !
1863 find_waitflag_info(wfx(_WF0,Store,_WFE1,_),Info,Prio,WF1) :-
1864 my_get_wf_store_att(Store,Heap),
1865 avl_member(Prio,Heap,wf(WF1,Info)).
1866
1867 % dereference the waitflag store, see SICStus for SPRM-20503
1868 % should be called e.g., in WHILE loops of interpreter to prevent degradation of put_atts performance
1869 deref_wf(wfx(WF0,S,EF,I),R) :- !, R=wfx(WF0,S2,EF,I), deref_store(S,S2).
1870 deref_wf(WF,WF).
1871
1872 % D is the dereferenced value of V.
1873 % see email by SICStus for SPRM-20503; gets rid of variable chains; was useful for old Waitflag store with attributes
1874 %deref(V,D) :- var(V), !, D = V.
1875 %deref(X,X).
1876
1877 % --------------------------
1878 % DEBUGGING UTILITIES:
1879
1880 % you also need to comment in code above using it
1881 /*
1882 :- dynamic debug_kernel_waitflags/1.
1883 set_debug_kernel_waitflags :- (debug_kernel_waitflags(_) -> true ; assertz(debug_kernel_waitflags(0))).
1884
1885 % the counter allows to set trace points
1886 get_debug_kernel_waitflags(Counter) :- retract(debug_kernel_waitflags(C)), C1 is C+1,
1887 assertz(debug_kernel_waitflags(C1)), Counter=C1.
1888
1889 get_debug_info(Info,Res) :- debug_kernel_waitflags(Counter),!,
1890 (Counter==39 -> trace ; true),
1891 Res=info(Counter,Info).
1892 get_debug_info(Info,Info).
1893 */
1894
1895 % check that a WF is grounded before EWF is grounded:
1896 :- public check_grounding/3. %debugging predicate
1897 check_grounding(WF,Info,LWF) :- get_enumeration_finished_wait_flag(WF,EWF), check_grounding_of_wf(WF,Info,LWF,EWF).
1898 :- block check_grounding_of_wf(?,?,-,-).
1899 check_grounding_of_wf(_,_,LWF,_) :- nonvar(LWF),!.
1900 check_grounding_of_wf(WF,Info,LWF,_EWF) :- add_internal_error('Waitflag not grounded: ',check_grounding(WF,Info,LWF)),
1901 portray_waitflags(WF),nl.
1902
1903 % --------------------------
1904
1905 :- assert_must_succeed(( kernel_waitflags:test_waitflags(1) )).
1906 :- assert_must_succeed(( kernel_waitflags:test_waitflags(2) )).
1907
1908 test_waitflags(1) :-
1909 init_wait_flags(Waitflags,[test_waitflags]),
1910 get_wait_flag(10,Waitflags,WF1),
1911 when(nonvar(WF1),print('WF1 - Prio 10\n')),
1912 get_wait_flag(5,Waitflags,WF2),
1913 when(nonvar(WF2),(print('WF2 - Prio 5\n'),
1914 get_wait_flag(6,Waitflags,WF4),
1915 when(nonvar(WF4),print('WF4 - Prio 6\n')),
1916 get_wait_flag(2,Waitflags,WF5),
1917 when(nonvar(WF5),print('WF5 - Prio2\n')))),
1918 get_wait_flag(5,Waitflags,WF3),
1919 when(nonvar(WF3),print('WF3 - Prio 5\n')),
1920 ground_wait_flags(Waitflags),nl.
1921
1922 test_waitflags(2) :-
1923 init_wait_flags(Waitflags,[test_waitflags]),
1924 get_wait_flag(10,Waitflags,WF1),
1925 when(nonvar(WF1),print('WF1 Prio 10\n')),
1926 get_wait_flag(5,Waitflags,WF2),
1927 when(nonvar(WF2),(print('WF2 - Prio 5\n'),
1928 get_wait_flag(6,Waitflags,WF4),
1929 when(nonvar(WF4),print('WF4 - Prio 6\n')),
1930 get_wait_flag(2,Waitflags,WF5),
1931 when(nonvar(WF5),print('WF5 - Prio2\n')))),
1932 get_wait_flag(5,Waitflags,WF3),
1933 when(nonvar(WF3),print('WF3 - Prio 5\n')),
1934 ground_wait_flags(Waitflags),nl.
1935
1936
1937 /*
1938
1939 bench_waitflags(N) :-
1940 statistics(walltime,[Tot1,_]),
1941 init_wait_flags(Waitflags),
1942 getwf(N,Waitflags),
1943 ground_wait_flags(Waitflags),nl,statistics(walltime,[Tot2,_]), Tot is Tot2-Tot1,
1944 format('Waitflag test for size ~w : ~w ms walltime (wf=~w)]~n',[N,Tot,Waitflags]).
1945 getwf(0,_).
1946 getwf(N,Waitflags) :- N>0, N1 is N-1, get_wait_flag(N,bench(N),Waitflags,_), getwf(N1,Waitflags).
1947
1948 % old attribute based:
1949 | ?- kernel_waitflags:bench_waitflags(1000).
1950
1951 Waitflag test for size 1000 : 37 ms walltime (wf=wfx(0,_205205,0,[]))]
1952 yes
1953 | ?- kernel_waitflags:bench_waitflags(10000).
1954
1955 Waitflag test for size 10000 : 5859 ms walltime (wf=wfx(0,_2512071,0,[]))]
1956 yes
1957
1958 % new mutable based:
1959 | ?- kernel_waitflags:bench_waitflags(1000).
1960
1961 Waitflag test for size 1000 : 4 ms walltime (wf=wfx(0,$mutable(wfm_store(empty,[],[]),3008),0,[]))]
1962 yes
1963 | ?- kernel_waitflags:bench_waitflags(10000).
1964
1965 Waitflag test for size 10000 : 24 ms walltime (wf=wfx(0,$mutable(wfm_store(empty,[],[]),30008),0,[]))]
1966 yes
1967 | ?- kernel_waitflags:bench_waitflags(100000).
1968
1969 Waitflag test for size 100000 : 497 ms walltime (wf=wfx(0,$mutable(wfm_store(empty,[],[]),9),0,[]))]
1970 yes
1971
1972 */