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