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