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