1 % (c) 2009-2019 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, waitflag0_is_set/1,
8 get_wait_flag_infos/2, get_wait_flag_info/2, is_wait_flag_info/2,
9 add_wait_flag_info/3,
10 get_wait_flag/3, get_wait_flag/4, get_bounded_wait_flag/4,
11 block_get_wait_flag/4,
12 get_wait_flag0/2,
13 get_wait_flag1/2, get_wait_flag1/3, % deterministic wait flag
14 get_middle_wait_flag/3, get_last_wait_flag/3,
15 get_idle_wait_flag/3,
16 get_binary_choice_wait_flag/3,
17 get_pow2_binary_choice_priority/2,
18 get_binary_choice_wait_flag_exp_backoff/3, get_binary_choice_wait_flag_exp_backoff/4,
19 %refresh_wait_flag/4,
20 add_fd_variable_for_labeling/3, add_fd_variable_for_labeling/2, add_fd_variables_for_labeling/2,
21 get_new_subsidiary_wait_flag/4,
22 update_waitflag/4,
23 ground_constraintprop_wait_flags/1,
24 check_is_wait_flag/1,
25 no_pending_waitflags/1,
26 create_inner_wait_flags/3, copy_wf01e_wait_flags/2,
27 copy_wf_start/2, copy_wf_finish/2,
28 copy_waitflag_store/2,
29
30 clone_wait_flags_from1/2, clone_wait_flags_from1_finish/2,
31 ground_wait_flag0/1,
32 %create_wdguarded_wait_flags/4,
33 %get_abort_error_flag/2,
34 get_large_finite_wait_flag/3,
35 get_enumeration_starting_wait_flag/3, ground_wait_flag_to_enumeration/1,
36 get_enumeration_finished_wait_flag/2,
37 get_integer_enumeration_wait_flag/3,
38 ground_det_wait_flag/1, ground_wait_flag_to/2,
39 ground_wait_flags/1, ground_inner_wait_flags/1,
40
41 add_abort_error_span/5,
42 add_wd_error_span/4,
43 add_wd_error/3, add_wd_error_set_result/6,
44 pending_abort_error/1, pending_abort_error/4,
45 %add_wd_error_span_now/3,
46 assert_must_abort_wf/2,
47
48 get_minimum_waitflag_prio/3,
49 portray_waitflags/1, quick_portray_waitflags/1, quick_portray_waitflags_store/1,
50 find_waitflag_info/4,
51
52 integer_priority/1, large_finite_priority/1, integer_pow2_priority/1,
53
54 my_get_kodkod_predicates/3, my_add_predicate_to_kodkod/3,
55 my_get_satsolver_predicates/3, my_add_predicate_to_satsolver/3,
56
57 set_silent/1]).
58 /* File: kernel_waitflags.pl */
59 /* Created: 17/3/06 by Michael Leuschel */
60
61 /*
62 Phases of the ProB Kernel:
63
64 -> all Waitflags unbound
65
66 * Phase 0: propagation of completely known ground values in a deterministic fashion
67
68 -> WF0 is bound
69
70 * Phase 1: deterministic propagation (not necessarily of completely known full values)
71
72 -> WF1 is bound
73
74 * Phase 2: boolean propagation (e.g., P or Q will now try P or Q)
75
76 -> WF2 is bound
77
78 * Phase 3: non-deterministic propagation (e.g, x: {1,2,3})
79
80 -> Enumeration Starting WF is bound
81
82 * Enumeration Phase: (later to be split into enumeration of finite and infinite types (e.g., integer)
83
84 -> Enumeration Finished Waitflag bound
85
86 * Error Generation Phase: errors for, e.g., division by zero, f(x) with x outside domain of f,... are generated
87 * This phase should not create choice points and should not detect failure
88 (e.g., in the
89 case of negated existential quantifiers the Enumeration Finished Waitflag will be set outside of delay_not)
90
91
92 We now distinguish
93 - Enumeration Wait Flags for Decision Variables with estimated enumeration domain size as priority
94 - Binary Choice Point Wait Flags (stemming e.g. from disjunctions)
95
96 Note: all floats like 1.0, 1.5, 2.0 ... are dealt with before all integers (even 1) !
97
98 */
99
100 :- use_module(error_manager).
101 :- use_module(self_check).
102 :- use_module(typechecker).
103 :- use_module(library(clpfd),[labeling/2, fd_size/2, fd_degree/2]).
104 :- use_module(tools).
105 :- use_module(coverage_tools_annotations,['$NOT_COVERED'/1]).
106 :- use_module(preferences,[preference/2]).
107
108 :- use_module(module_information,[module_info/2]).
109 :- module_info(group,kernel).
110 :- module_info(description,'This module manages the various choice points and enumerators of ProB, by maintaining the kernel Waitflags store.').
111
112 :- type wait_flag +--> (var ; wfx(any,var,any,list(any)) ; no_wf_available).
113
114 :- use_module(library(avl)).
115 :- use_module(library(atts)).
116
117 :- attribute wf_store/1,wf_fdvars/1, wf_kodkod/2, wf_satsolver/2, wf_abort_pending/0.
118
119 % the Call must succeed and generate one abort_error
120 :- meta_predicate assert_must_abort_wf(0,*).
121 %:- meta_predicate assert_must_abort2_wf(0,-).
122 assert_must_abort_wf(M:Call,WF) :- assert_must_succeed_any(M:(kernel_waitflags:assert_must_abort2_wf(M:Call,WF))).
123
124 assert_must_abort2_wf(_,_) :- get_error(well_definedness_error,M),
125 add_internal_error('Abort error prior to call in assert_must_abort: ',M),fail.
126 assert_must_abort2_wf(Call,WF) :- (real_error_occurred -> RE=true ; RE=false),
127 init_wait_flags(WF),
128 call(Call), %print(called(Call)),nl,
129 (ground_wait_flags(WF)
130 -> add_internal_error('Call did not fail after grounding WF: ',assert_must_abort2_wf(Call,WF)),fail
131 ; (get_error(well_definedness_error,_), % we have 1 abort_error
132 print(got_expected_well_definedness_error),nl,
133 (RE=false -> reset_real_error_occurred ; true)
134 )
135 ).
136
137 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF), kernel_waitflags:check_is_wait_flag(WF) )).
138
139 check_is_wait_flag(WFX) :- var(WFX),!,
140 add_internal_error('Variable as waitflag: ',check_is_wait_flag(WFX)).
141 check_is_wait_flag(WFX) :-
142 WFX = wfx(_,Store,_,_)
143 -> (get_atts(Store,+wf_store(_))
144 -> true
145 ; print('WF does not yet contain heap: '), print(WFX),nl)
146 %add_internal_error('WF does not contain heap: ',check_is_wait_flag(WFX)))
147 ; add_internal_error('Illegal WF Format: ',check_is_wait_flag(WFX)).
148
149 verify_attributes(Var, Value, [] ) :- get_atts(Var,+wf_abort_pending),!,
150 % we are unifying the EWF enumeration finished waitflag, not the store
151 (nonvar(Value) -> true ; print(verify_attributes),nl).
152 verify_attributes(Var, Value, [] ) :-
153 nonvar(Value) -> print('Trying to unify waitflag_heap_store'),nl
154 ; print(verify_attributes(Var,Value)),nl.
155
156 init_wait_flags(wfx(_,_,_,[])).
157 %init_wait_flags(wfx(_,Store,_,[])) :- init_wait_flags_store(Store).
158 % we no longer do this because of performance, (see, e.g., LotsOfInvariants.mch) :
159 init_wait_flags_store(_).
160 %init_wait_flags_store(Store) :-
161 % empty_avl(Heap),
162 % put_atts(Store,+wf_store(Heap)),
163 % put_atts(Store,+wf_fdvars([])).
164
165
166 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF),
167 kernel_waitflags:ground_wait_flag0(WF), kernel_waitflags:waitflag0_is_set(WF) )).
168 :- assert_must_fail(( kernel_waitflags:init_wait_flags(WF), kernel_waitflags:waitflag0_is_set(WF) )).
169 waitflag0_is_set(wfx(WF0,_Store,_EF,_INFOS)) :- ground(WF0).
170 waitflag0_is_set(no_wf_available).
171
172 get_new_subsidiary_wait_flag(OldWF,Info,WFX,NewWF) :-
173 (ground(OldWF) /* then we have created a choice point with the old WF: double new WF prio */
174 -> NewPrio is OldWF*2, get_wait_flag(NewPrio,Info,WFX,NewWF)
175 ; NewWF = OldWF
176 ).
177
178 % not used at the moment:
179 %refresh_wait_flag(OldWF,Info,WFX,NewWF) :-
180 % (ground(OldWF) /* then we have created a choice point with the old WF: double new WF prio */
181 % -> get_wait_flag(OldWF,Info,WFX,NewWF)
182 % ; NewWF = OldWF
183 % ).
184
185 :- volatile silent/0.
186 :- dynamic silent/0.
187 set_silent(true) :- !,(silent -> true ; assert(silent)).
188 set_silent(false) :- retractall(silent).
189 %waitflag_not_init :- silent -> true ; print('waitflag-store not initialised'),nl, '$NOT_COVERED'('This should not happen').
190
191 :- use_module(eventhandling,[register_event_listener/3]).
192 :- register_event_listener(start_unit_tests,set_silent(true),'Allow wf not to be set up without printing a warning').
193 :- register_event_listener(stop_unit_tests, set_silent(false), 'Printing wf warnings again').
194
195 :- use_module(library(lists),[maplist/2]).
196 add_fd_variables_for_labeling(Vars,WF) :- WF==no_wf_available,!,
197 add_internal_error('Cannot add FDVars to waitflag store: ',Vars).
198 add_fd_variables_for_labeling(Vars,WF) :-
199 WF=wfx(_,Store,_EnumFinished,_INFOS),
200 !,
201 my_get_fdvars_atts(Store,FDVARS),
202 l_add_fd_var_to_FDVARS(Vars,FDVARS,NewFDVARS),
203 put_atts(Store,+wf_fdvars(NewFDVARS)).
204 add_fd_variables_for_labeling(Vars,WF) :-
205 add_internal_error('Illegal waitflag store: ',add_fd_variables_for_labeling(Vars,WF)).
206
207 l_add_fd_var_to_FDVARS([],Acc,Acc).
208 l_add_fd_var_to_FDVARS([Var|VT],FDVARS,NewFDVARS) :-
209 (nonvar(Var) -> F2=FDVARS; add_fd_var_to_FDVARS(Var,FDVARS,F2)),
210 l_add_fd_var_to_FDVARS(VT,F2,NewFDVARS).
211
212 % adds a CLP(FD) variable with given domain Size, will be enumerated by labeling (even if clpfd_solver preference is false !)
213 % Size could be computed by fd_size(FDVariable,Size)
214 add_fd_variable_for_labeling(FDVariable,WF) :-
215 add_fd_variable_for_labeling_aux(FDVariable,WF).
216 add_fd_variable_for_labeling(FDVariable,Size,WF) :- Size = not_used_anymore,
217 add_fd_variable_for_labeling_aux(FDVariable,WF).
218
219
220 add_fd_variable_for_labeling_aux(FDVariable,_WF) :- nonvar(FDVariable),!.
221 add_fd_variable_for_labeling_aux(FDVariable,WF) :-
222 % otherwise: store the FDVariable in the separate list of FD Variables (all mixed together)
223 WF=wfx(_,Store,_EnumFinished,_),
224 my_get_fdvars_atts(Store,FDVARS),
225 add_fd_var_to_FDVARS(FDVariable,FDVARS,NewFDVARS),
226 %% fd_size(FDVariable,Size),print(added(FDVariable,Size,NewFDVARS,old(FDVARS))),nl,
227 %% print(added_fd_variable(FDVariable,_Size,NewFDVARS)),nl,portray_waitflags_store(Store),nl,nl,%%
228 put_atts(Store,+wf_fdvars(NewFDVARS)).
229
230 add_fd_var_to_FDVARS(FDVariable,FDVARS,NewFDVARS) :-
231 %insert_fd_var(FDVARS,FDVariable,Size,LWF,NewFDVARS),
232 (FDVARS=[HH|_],HH==FDVariable -> NewFDVARS = FDVARS % variable already in list [cheap check at front]
233 ; NewFDVARS = [FDVariable|FDVARS]). %% this adds new variables at front; will be given priority over older ones
234 %% add_fd_var(FDVARS,FDVariable,NewFDVARS), %% this adds new variables at the end and does more thorough duplicate check
235
236
237 % a bit like append: but cleans up list: removes numbers + checks if var already in list
238 % runtimes for tests: 349 stays at 220ms, test 1088 4660 -> 5040ms
239 %add_fd_var([],Var,[Var]).
240 %add_fd_var([V|T],Var,Res) :-
241 % (V==Var -> Res = [V|T] %,print(dup(V)),nl,
242 % ; nonvar(V) -> add_fd_var(T,Var,Res) %,print(nonvar(V)),nl
243 % ; Res = [V|RT], add_fd_var(T,Var,RT)).
244
245 my_get_fdvars_atts(Store,FDVARS) :-
246 (get_atts(Store,+wf_fdvars(FDVARS)) -> true
247 ; var(Store),FDVARS=[]
248 ).
249 /* store not initialised; initialise it below but print msg */
250 % ,waitflag_not_init).
251
252 :- use_module(library(random),[random_select/3]).
253 % reverse the list at each step; alternating taking from end and front; makes no sense with randomise_enumeration_order
254 %my_get_next_fdvar_to_enumerate_rev(FDVARS,NextFDVar,RemainingFDVARS) :- lists:reverse(FDVARS,RevFDVARS),
255 % my_get_next_fdvar_to_enumerate(RevFDVARS,NextFDVar,RemainingFDVARS).
256
257 my_get_next_fdvar_to_enumerate([],_NextFDVar,_RemainingFDVARS) :- !,fail.
258 my_get_next_fdvar_to_enumerate([V1|FDVARS],NextFDVar,RemainingFDVARS) :-
259 nonvar(V1),
260 !, % 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 !
261 my_get_next_fdvar_to_enumerate(FDVARS,NextFDVar,RemainingFDVARS).
262 my_get_next_fdvar_to_enumerate(FDVARS,NextFDVar,RemainingFDVARS) :-
263 preferences:preference(randomise_enumeration_order,true),
264 !,
265 get_min_fd_size_elements(FDVARS,MinFDVars),
266 random_select(NextFDVar,MinFDVars,_),
267 %print(random_enum(NextFDVar,FDVARS,MinFDVars)),nl,
268 RemainingFDVARS=FDVARS. % still contains variable; but no problem as it will be discarded later
269 my_get_next_fdvar_to_enumerate(FDVARS,NextFDVar,RemainingFDVARS) :-
270 %clpfd_interface:clpfd_get_next_variable_to_label_ffc(FDVARS,NextFDVar,RemainingFDVARS).
271 clpfd_interface:clpfd_get_next_variable_to_label(FDVARS,NextFDVar),
272 % print(enumerating(NextFDVar,FDVARS)),nl, maplist(tools_printing:print_arg,FDVARS),nl,
273 RemainingFDVARS=FDVARS.
274 % ((FDVARS=[H|T],H==NextFDVar) -> RemainingFDVARS=T ; RemainingFDVARS=FDVARS).% does not seem to buy any speed
275
276 get_min_fd_size_elements([V1|FDVARS],MinFDVars) :- my_get_fd_size(V1,Size),
277 get_min_aux(FDVARS,Size,[V1],MinFDVars).
278
279 get_min_aux([],_,MinFDVars,MinFDVars).
280 get_min_aux([V1|FDVARS],Size,MAcc,MinFDVars) :- number(V1),!,
281 get_min_aux(FDVARS,Size,MAcc,MinFDVars).
282 get_min_aux([V1|FDVARS],Size,MAcc,MinFDVars) :-
283 my_get_fd_size(V1,Size1),
284 (lt_size(Size1,Size)
285 -> get_min_aux(FDVARS,Size1,[V1],MinFDVars)
286 ; Size1=Size -> get_min_aux(FDVARS,Size,[V1|MAcc],MinFDVars)
287 ; get_min_aux(FDVARS,Size,MAcc,MinFDVars)
288 ).
289
290 lt_size(X,Y) :- number(X),(Y=sup -> true ; X<Y).
291
292 % -------------
293
294 % using this is relevant for e.g. tests 415, 416, 1096 : to give variables without frozen goals a lower priority
295 % TO DO: we could filter out those before we call clpfd_get_next_variable_to_label !?
296 fd_priority_leq_limit(FDVAR,Prio,Limit) :-
297 my_get_fd_size(FDVAR,Size),
298 number(Size), % not =sup or =inf
299 geq_limit(Limit,Size), % the calculation below will only increase the Size
300 (Size < 2 -> Prio = Size
301 ; geq_limit(Limit,Size*4) -> Prio = Size % avoid calling frozen and fd_degree, Priority only used for warnings and bisect decisions
302 ; %print(sz(Size,Limit)),nl,
303 (enumeration_only_fdvar(FDVAR) -> Prio is Size*4, Prio =< Limit
304 ; Prio = Size)
305 ).
306
307 my_get_fd_size(FDVAR,ResSize) :- fd_size(FDVAR,Size),!, ResSize=Size.
308 %(Size=sup -> ResSize = 10000000 /* same as for inf */ ; ResSize=Size).
309 my_get_fd_size(FDVAR,Size) :- add_internal_error('Could not get fd_size: ',my_get_fd_size(FDVAR,Size)), Size=sup.
310
311 gt_limit(inf,_) :- !.
312 gt_limit(sup,_) :- !.
313 gt_limit(X,Limit) :- X > Limit.
314
315 geq_limit(inf,_) :- !.
316 geq_limit(sup,_) :- !.
317 geq_limit(X,Limit) :- X >= Limit.
318
319
320
321 % -------------
322
323
324 enumeration_only_fdvar(FDVAR) :- var(FDVAR),fd_degree(FDVAR,D),!,
325 D=0,
326 frozen(FDVAR,Goal), %print(frozen(FDVAR,D,Goal)),nl,
327 enum_only_frozen_goal(Goal,FDVAR).
328 enumeration_only_fdvar(_).
329
330 % TO DO: maybe use enumeration_only_goal in b_enumerate
331 enum_only_frozen_goal((A,B),Var) :- !, enum_only_frozen_goal(A,Var), enum_only_frozen_goal(B,Var).
332 enum_only_frozen_goal(clpfd:_,_) :- !.
333 enum_only_frozen_goal(kernel_objects:G,V) :- !, enum_only_frozen_goal_k_obj(G,V).
334 enum_only_frozen_goal(true,_).
335
336 enum_only_frozen_goal_k_obj(safe_less_than_equal(_,X,Y),_) :- (number(X);number(Y)), !.
337 enum_only_frozen_goal_k_obj(call_enumerate_int(X,_,_,_),Var) :- Var==X,!.
338 enum_only_frozen_goal_k_obj(enumerate_int_wf(X,_,_),Var) :- Var==X,!.
339 enum_only_frozen_goal_k_obj(true,_).
340
341
342 % get a wait-flag that will be triggered first next time;
343 % can be used to ensure that all pending co-routines complete
344 get_idle_wait_flag(Info,wfx(WF0,Store,EnumFinished,_),LWF) :- !,
345 (var(WF0) -> LWF=WF0
346 ; nonvar(EnumFinished) -> LWF=1 % waitflag store already grounded to completion; nobody may drive it anymore
347 ; get_waitflag_from_store(0.9,Info,Store,LWF)).
348 get_idle_wait_flag(_Info,no_wf_available,1).
349
350
351 get_wait_flag(Prio,Store,WF) :- get_wait_flag(Prio,'??',Store,WF).
352 get_wait_flag(0,_Info,wfx(WF0,_Store,_EnumFinished,_),WF) :- !, WF=WF0.
353 get_wait_flag(1.0,Info,wfx(WF0,Store,_EnumFinished,_),WF) :- !,
354 (var(WF0) -> get_waitflag_from_store(1.0,Info,Store,WF) ; WF=1).
355 get_wait_flag(Prio,Info,wfx(_,Store,EnumFinished,_),WF) :- !,
356 (ground(EnumFinished) -> WF=Prio /* enumeration has finished: return a ground WF */
357 ; get_waitflag_from_store(Prio,Info,Store,WF)).
358 get_wait_flag(Prio,Info,Store,WF) :-
359 (Store=no_wf_available -> WF=Prio ;
360 Store=none -> print(get_wait_flag_deprecated(Prio,Info,Store,WF)),nl, WF=Prio ;
361 add_internal_error('Illegal call: ',get_wait_flag(Prio,Info,Store,WF))).
362
363 get_waitflag_from_store(Prio,Info,Store,WF) :-
364 %get_debug_info(Info0,Info),
365 % print(get_waitflag_from_store(Prio,Info,Store,WF)),nl,
366 (Prio=inf -> integer_priority(X), RealPrio = float(X)
367 % , print(infinite_priority(Prio,Info)),nl %%
368 % ; (Prio>10, Prio<10000) -> RealPrio = float(100) % perform left-to-right enumeration in that range
369 ; RealPrio=float(Prio)),
370 my_get_wf_store_atts(Store,Heap),
371 (avl_fetch(RealPrio,Heap,wf(WFs,_OldInfo))
372 -> (is_finite_priority(RealPrio) % TO DO: investigate performance if reduce the threshold
373 -> WF1 = WFs % here we re-use the waitflag instead of storing a new one
374 ; (var(WFs) -> WFs2 = [WF1,WFs] ; WFs2 = [WF1|WFs]), % else: store waitflags as list; important for test 1194
375 %print(not_reusing(WFs2)),nl,
376 avl_store(RealPrio,Heap,wf(WFs2,Info),NewHeap),
377 put_atts(Store,+wf_store(NewHeap))
378 )
379 ; avl_store(RealPrio,Heap,wf(WF1,Info),NewHeap),
380 put_atts(Store,+wf_store(NewHeap))
381 ),!,
382 %avl_size(NewHeap,Sz),
383 %(Sz=10 -> print(heap_size(10)),nl ; true),
384 %(Sz=1000 -> print(heap_size(1000)),nl, portray_avl(NewHeap) ; true),
385 WF=WF1.
386 get_waitflag_from_store(Prio,Info,Store,WF) :-
387 add_internal_error('Error getting Waitflag: ', get_waitflag_from_store(Prio,Info,Store,WF)),fail.
388
389 % a version which delays getting a waitflag until the Prio is known:
390 :- block block_get_wait_flag(-,?,?,?).
391 block_get_wait_flag(Prio,Info,WFX,WF) :- get_wait_flag(Prio,Info,WFX,WF).
392
393 % check if there are no waitflags and no FD variables; note: there could be pending co-routines on WF0
394 no_pending_waitflags(no_wf_available).
395 no_pending_waitflags(wfx(_WF0,Store,_WFE,_)) :-
396 my_get_wf_store_atts(Store,Heap), empty_avl(Heap), % no waitflags pending
397 (get_atts(Store,+wf_fdvars(FDVars)) -> FDVars == [] ; true). % and no FD Variables either
398
399 % -------------------
400
401 my_get_wf_store_atts(Store,Heap) :-
402 (get_atts(Store,+wf_store(Heap)) -> true
403 ; var(Store),empty_avl(Heap)
404 ).
405 %, /* store not initialised; initialise it below but print msg */
406 % waitflag_not_init).
407
408 % -------------------
409
410 % storing Predicates for Kodkod external function:
411 my_get_kodkod_predicates(wfx(_,Store,_,_),ID,PredicateList) :- my_get_wf_kodkod(Store,ID,PredicateList).
412 my_get_wf_kodkod(Store,ID,PredicateList) :-
413 (get_atts(Store,+wf_kodkod(ID,PredicateList)) -> true
414 ; var(Store),PredicateList = []
415 ).
416 %my_store_wf_kodkod(Store,ID,PredicateList) :- put_atts(Store,+wf_kodkod(ID,PredicateList)).
417 my_add_predicate_to_kodkod(wfx(_,Store,_,_),ID,Predicate) :-
418 my_get_wf_kodkod(Store,ID,PredicateList),
419 put_atts(Store,+wf_kodkod(ID,[Predicate|PredicateList])).
420
421 % -------------------
422
423 % storing Predicates for external function calling satsolver:
424 my_get_satsolver_predicates(wfx(_,Store,_,_),ID,PredicateList) :- my_get_wf_satsolver(Store,ID,PredicateList).
425 my_get_wf_satsolver(Store,ID,PredicateList) :-
426 (get_atts(Store,+wf_satsolver(ID,PredicateList)) -> true
427 ; var(Store),PredicateList = []
428 ).
429 my_add_predicate_to_satsolver(wfx(_,Store,_,_),ID,Predicate) :-
430 my_get_wf_satsolver(Store,ID,PredicateList),
431 put_atts(Store,+wf_satsolver(ID,[Predicate|PredicateList])).
432
433 % -------------------
434
435
436 ground_wait_flag0(wfx(WF0,_,_,_)) :- grd_wf0(WF0).
437
438 ?ground_wait_flags(wfx(WF0,Store,EnumFinish,_)) :- !,
439 ? grd_wf0(WF0),
440 ? ground_waitflags_store_clpfd(Store),
441 grd_ef(EnumFinish).
442 ground_wait_flags(no_wf_available) :- !.
443 ground_wait_flags(W) :-
444 add_internal_error('Waitflags in wrong format: ',ground_wait_flags(W)).
445
446 ?grd_wf0(E) :- var(E) -> E=0 %%,print(grd_wf0_done),nl
447 ; (E==0 -> true ; add_internal_error('Illegal WF0 Waitflag value: ',grd_wf0(E))).
448 grd_ef(E) :- var(E) -> E=0
449 ; ((E==0;E==wd_guarded) -> true ; add_internal_error('Illegal EF Waitflag value: ',grd_ef(E))).
450
451 /* currently unused:
452 create_wdguarded_wait_flags(wfx(WF0,Store,EnumFinish,Info),Res,Expected,
453 wfx(WF0,Store,LocalEnumFinish,Info)) :-
454 %% if Res=Expected then the computations of the inner waitflags should be well-defined;
455 %% otherwise they may not
456 copy_wfe(EnumFinish,Res,Expected,LocalEnumFinish).
457 :- block copy_wfe(-,?,?,?), copy_wfe(?,-,?,?).
458 copy_wfe(EnumFinish,Res,Expected,LocalEnumFinish) :-
459 (Res==Expected -> LocalEnumFinish=EnumFinish
460 ; LocalEnumFinish=wd_guarded %% branch is pruned by well-definedness condition
461 ).
462 */
463
464 % will not set WFE flag
465 ground_constraintprop_wait_flags(wfx(WF0,Store,_WFE,_Infos)) :-
466 %% print(ground_constraintprop_wait_flags(WF0,Store,_WFE)),nl, %%
467 ? !,grd_wf0(WF0),% print(finished_grounding_wf0),nl,portray_waitflags_store(Store),
468 ? ground_waitflags_store_clpfd(Store).
469 ground_constraintprop_wait_flags(no_wf_available) :- !.
470 ground_constraintprop_wait_flags(W) :-
471 add_internal_error('Waitflags in wrong format',ground_constraintprop_wait_flags(W)).
472
473 :- use_module(clpfd_interface,[clpfd_in_domain/1, clpfd_labeling/2]).
474 :- use_module(library(lists),[reverse/2]).
475
476 %ground_wf(Prio,WF) :- nl,print(ground(WF,Prio)),nl,WF=Prio, print(done_ground(WF,Prio)),nl.
477 % ground_wf(Prio,WF) :- Prio=WF.
478 ?ground_wf(Prio,WF) :- (var(WF) -> WF=Prio ; if(WF=Prio,true,add_internal_error('Illegal waitflag: ',ground_wf(Prio,WF)))).
479
480 ground_prio(Prio,WF,Info) :- %print(ground_prio(Prio,WF,Info)),nl,
481 ? nonvar(WF),!,
482 ? (WF=[H|T] -> % we have a list of waitflags; ground one-by one; TO DO: only ground one and store remaining list in wfx Heap
483 ? reverse([H|T],Ls), %print(list(Ls)),nl,
484 ? maplist(ground_wf(Prio),Ls)
485 ; ground(WF) -> true % already grounded by somebody else
486 ; add_internal_error('Waitflag already partially grounded: ',ground_prio(Prio,WF,Info))).
487 %ground_prio(Prio,WF,Info) :- debug_kernel_waitflags(Cntr),!,
488 % print(ground_prio(Cntr,Prio,WF,Info)),nl, WF=Prio, print(grounded_prio(Cntr,Prio,WF,Info)),nl.
489 ground_prio(Prio,WF,_Info) :-
490 %external_functions:indent_format(user_output,'~w (~w)~n',[Prio,Info],no_trace),portray_attached_goals(WF),nl,
491 %% (debug:debug_mode(on) -> external_functions:indent_format(user_output,'~w (~w)~n',[Prio,Info],no_trace) ; true),
492 % print(ground(Prio,WF,Info)),nl, translate:print_frozen_info(WF),nl,
493 ? WF = Prio. % ground waitflag
494 % print(grounded(Prio,Info)),nl,nl, %%
495
496 %:- use_module(kernel_objects,[enum_warning_large/3]).
497 ?label_clpfd_variable(Variable) :- label_clpfd_variable(2097153,Variable).
498 label_clpfd_variable(Prio,Variable) :-
499 ? (gt_limit(Prio,2097152), % note Prio > test also works with float(X) terms !
500 check_if_labeling_domain_large(Variable,2097152,Size,RANGE) % {x|x:1..2**23 & x mod 2 = x mod 1001} takes about 2 minutes; {x|x:1..2**21 & x mod 2 = x mod 1001} takes about 30 seconds; 2**21 = 2097152, 2**23 = 8388608
501 -> %kernel_objects:enum_warning_large(Variable,'INTEGER VARIABLE',RANGE), % could in principle also be enumerated set; but extremely unlikely given size; enum_warning will generate virtual time-outs, ... -> we no longer do this; for AMASS scheduler.mch there is a time-out after step 301 otherwise
502 ? performance_messages:perfmessage(enumerating_large_integer_variable(RANGE)),
503 ? (number(Size)
504 ? -> clpfd_labeling([Variable],[bisect]) % ffc no longer required: just a single variable
505 ; add_warning(label_clpfd_variable,'Trying to enumerate FD Variable with infinite domain (possibly due to WD error) ',RANGE)
506 )
507 % we use bisecting rather than default enumeration; e.g., x:1..8388610 & y:1..8388610 & x*x*x = y*y + y + 10 is relatively quickly determined to be unsatisfiable (380 ms)
508 % Note: test 1099 tests this
509 % TO DO: investigate whether it makes sense to use bisect for smaller values already
510 ? ; preferences:preference(randomise_enumeration_order,true) -> clpfd_interface:clpfd_randomised_in_domain(Variable)
511 ? ; clpfd_in_domain(Variable)). % TO DO: use random ?
512
513 check_if_labeling_domain_large(H,Limit,Size,Res) :-
514 my_get_fd_size(H,Size),
515 gt_limit(Size,Limit),
516 clpfd_interface:clpfd_domain(H,From,To),
517 Res=From:To.
518
519 %check_if_labeling_domain_finite(H) :-
520 % my_get_fd_size(H,Size), number(Size).
521
522 % mix ProB labeling with CLP(FD) labeling
523 ground_waitflags_store_clpfd(Store) :-
524 ? my_get_wf_store_atts(Store,Heap),!,
525 ? ground_waitflags_store_clpfd_aux(Store,Heap).
526 ground_waitflags_store_clpfd(Store) :-
527 add_internal_error('Illegal Waitflag: ',ground_waitflags_store_clpfd(Store)),fail.
528
529 ground_waitflags_store_clpfd_aux(Store,Heap) :-
530 %my_get_fdvars_atts(Store,FDVars),
531 ? get_atts(Store,+wf_fdvars(FDVars)), % there is a list of FD vars available
532 ? !,
533 ? ( avl_del_min(Heap,Prio,wf(WF,Info),NewHeap) -> % we have non-ground waitflags with a matching priority
534 ? (my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS),
535 fd_priority_leq_limit(NextFDVar,FDPrio,Prio)
536 ? -> enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS)
537 ? ; put_atts(Store,+wf_store(NewHeap)), % write the modified heap back
538 % debug:hit_counter(XXX),print(grounding(XXX,Prio,_Info)),nl, (XXX=16556->trace ; true),
539 %WF = Prio, % ground waitflag
540 % print(grounding(Prio,Info)),nl,
541 ? ground_prio(Prio,WF,Info),
542 ? ground_waitflags_store_clpfd(Store) % continue recursively
543 )
544 ; /* otherwise -> */ %print(empty_store),nl,
545 ? (my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS)
546 ? -> enum_fd_variable_store(Store,NextFDVar,RemainingFDVARS)
547 ; true % heap and FDVars list is empty, nothing more to do
548 )
549 ).
550 ground_waitflags_store_clpfd_aux(Store,Heap) :- % no FDVars are available to enumerate
551 ? avl_del_min(Heap,Prio,wf(WF,Info),NewHeap), !,
552 ? put_atts(Store,+wf_store(NewHeap)), % write the modified heap back
553 % debug:hit_counter(XXX),print(grounding(XXX,Prio,_Info)),nl, (XXX=16556->trace ; true),
554 ? ground_prio(Prio,WF,Info), % ground waitflag
555 ? ground_waitflags_store_clpfd(Store). % continue recursively
556 ground_waitflags_store_clpfd_aux(_Store,_Heap). % heap and FDVars list is empty, nothing more to do
557
558 %:- use_module(library(lists),[exclude/3]).
559 enum_fd_variable_store(Store,NextFDVar,RemainingFDVARS) :-
560 %exclude(nonvar,RemainingFDVARS,RemainingFDVARS2),
561 ? put_atts(Store,+wf_fdvars(RemainingFDVARS)),
562 %% print(labeling(NextFDVar,RemainingFDVARS)),nl, portray_fd_vars([NextFDVar|RemainingFDVARS]),nl,nl,
563 ? label_clpfd_variable(NextFDVar),
564 ? ground_waitflags_store_clpfd(Store).
565 % a version of the above where we know the priority
566 enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS) :-
567 %exclude(nonvar,RemainingFDVARS,RemainingFDVARS2), % does not seem to buy a lot
568 ? put_atts(Store,+wf_fdvars(RemainingFDVARS)),
569 %% print(labeling(NextFDVar,RemainingFDVARS)),nl, portray_fd_vars([NextFDVar|RemainingFDVARS]),nl,nl,
570 ? label_clpfd_variable(FDPrio,NextFDVar),
571 ? ground_waitflags_store_clpfd(Store).
572
573 :- if(false).
574 % small utility to trace through waitflag enumeration
575 :- dynamic spying_on/1.
576 spying_on(1).
577 spy_waitflags(_,_) :- \+ spying_on(_) , !.
578 spy_waitflags(Msg,Store) :- retract(spying_on(Nr)),print(Msg),nl,
579 (Nr=1
580 -> assert(spying_on(Nr)),
581 print(' (j,t,p,q) ==> '),read_term(T,[]),
582 action(T,Store)
583 ; N1 is Nr-1, assert(spying_on(N1))
584 ).
585 action(t,_Store) :- !,trace.
586 action(p,Store) :- !,portray_waitflags_store(Store), spy_waitflags('',Store).
587 action(q,_Store) :- !,retractall(spying_on).
588 action(Nr,_) :- number(Nr),!, retractall(spying_on(_)), assert(spying_on(Nr)).
589 action(_,_).
590 :- endif.
591
592 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF), kernel_waitflags:portray_waitflags(WF),
593 kernel_waitflags:get_wait_flag(2,'Test',WF,WF2), when(nonvar(WF2),print(wf2)),
594 clpfd:in(FD,'..'(1,2)),kernel_waitflags:add_fd_variable_for_labeling(FD,WF),
595 kernel_waitflags:portray_waitflags(WF), kernel_waitflags:ground_wait_flags(WF) )).
596 % PORTRAYING Waitflags
597 portray_waitflags(wfx(WF0,Store,WFE,Infos)) :- flush_output,
598 print('*WAITFLAG STORE: '),print(wfx(WF0,Store,WFE,Infos)),nl,
599 (var(WF0) -> print('> WF0 :: '), portray_attached_goals(WF0),nl ; true),
600 portray_waitflags_store(Store),!,
601 (var(WFE) -> print('> WFE :: '), portray_attached_goals(WFE),nl ; true),
602 print('*END WAITFLAG STORE'),nl.
603 %my_portray_avl(Heap),nl.
604 portray_waitflags(none) :- !, print('*WAITFLAG STORE: none'),nl.
605 portray_waitflags(no_wf_available) :- !, print('*WAITFLAG STORE: none'),nl.
606 portray_waitflags(Store) :-
607 add_internal_error('Illegal Waitflag: ',portray_waitflags(Store)),flush_output.
608
609 portray_fd_vars([]) :- !, '$NOT_COVERED'('This is debugging code!').
610 portray_fd_vars([V|T]) :- print('> '), print(V), nonvar(V),!, nl,
611 portray_fd_vars(T), '$NOT_COVERED'('This is debugging code!').
612 portray_fd_vars([V|T]) :- my_get_fd_size(V,Size), !, print(' : '), print(Size),
613 portray_attached_goals(V),
614 portray_fd_vars(T),
615 '$NOT_COVERED'('This is debugging code!').
616 portray_fd_vars(X) :- print('> *** UNKNOWN FDVARS: '), print(X), nl,
617 '$NOT_COVERED'('This is debugging code!').
618
619 portray_waitflags_store(Store) :-
620 my_get_fdvars_atts(Store,FDList),
621 (FDList=[] -> true ; print('FDVARS:'),nl,portray_fd_vars(FDList),nl),
622 my_get_wf_store_atts(Store,Heap),
623 avl_to_list(Heap,List), portray_avl_els(List).
624
625 portray_avl_els([]).
626 portray_avl_els([Prio-wf(LWF,Info)|T]) :-
627 print('> '),print(Prio), print(' : '), print(LWF), print(' : '), portray_info(Info),nl,
628 portray_attached_goals(LWF),
629 portray_avl_els(T).
630
631 portray_info(label_clpfd_vars(Vars)) :- print('label_clpfd_vars :: '),
632 translate:l_print_frozen_info(Vars).
633 portray_info(I) :- print(I).
634
635 portray_attached_goals(V) :- V==[],!.
636 portray_attached_goals(LWF) :- nonvar(LWF),LWF=[H|T],!,
637 portray_attached_goals(H),portray_attached_goals(T).
638 portray_attached_goals(LWF) :- frozen(LWF,Goal), print(' :: '), portray_goal(Goal),nl.
639
640 :- use_module(tools_printing,[print_term_summary/1]).
641 portray_goal((A,B)) :- !, portray_goal(A), print(','), print(' '), portray_goal(B).
642 portray_goal(A) :- print_term_summary(A).
643
644 :- public my_portray_avl/1.
645 my_portray_avl(V) :- var(V), !, add_internal_error('Variable: ',my_portray_avl(V)).
646 my_portray_avl(V) :- portray_avl(V).
647
648
649 quick_portray_waitflags(wfx(WF0,Store,WFE,_)) :-
650 my_get_fdvars_atts(Store,FDList),
651 my_get_wf_store_atts(Store,Heap),
652 avl_domain(Heap,List),
653 format('% Waitflags-Store WF0=~w,WFE=~w, Flags=~w, FDVars=~w~n',[WF0,WFE,List,FDList]).
654 quick_portray_waitflags_store(Store) :-
655 my_get_fdvars_atts(Store,FDList),
656 my_get_wf_store_atts(Store,Heap),
657 avl_domain(Heap,List),
658 format('% Waitflags-Store Flages=~w, FDVars=~w~n',[List,FDList]).
659
660 :- assert_must_succeed(( kernel_waitflags:init_wait_flags(WF),
661 kernel_waitflags:get_wait_flag1(WF,WF1), when(ground(WF1),X=a),
662 kernel_waitflags:ground_wait_flags(WF), X==a )).
663
664 % create a copy of the waitflags with WF0 and WF0 as variable; should be finished with copy_wf_finish
665 % can be used if you temporarily want to disable non-deterministic enumeration, ...
666 copy_wf_start(wfx(_,WF,_,Infos),wfx(_,WF,_,Infos)).
667 copy_wf_start(no_wf_available,no_wf_available).
668
669 % just copy WF0 and WFE over to new waitflag
670 copy_wf_finish(wfx(WF0,_,WFE,_),wfx(WF0,_,WFE,_)).
671 copy_wf_finish(no_wf_available,no_wf_available).
672
673
674 /* create a copy where the enumeration_finished waitflag is shared;
675 the inner enumeration_finished wait flags should normally not be grounded */
676 %create_inner_wait_flags(wfx(_,_,WFE),wfx(WF0Inner,S,WFE)) :- !,init_wait_flags_store(S). % old version
677 create_inner_wait_flags(wfx(_,_,WFE,Infos),Info,wfx(WF0Inner,S,WFEInner,Infos)) :-
678 init_wait_flags_store(S),
679 get_inner_enumeration_over_wait_flag(wfx(WF0Inner,S,WFEInner,Infos),Info,LastWF),
680 copy_wfe_to_inner(LastWF,WFE,WFEInner). % delay copying WFE at least until inner enumeration has been run
681 create_inner_wait_flags(no_wf_available,_,no_wf_available).
682 :- block copy_wfe_to_inner(-,?,?).
683 copy_wfe_to_inner(_,WFE,WFE).
684
685 ground_inner_wait_flags(wfx(WF0,Store,_EWF,_)) :- !,
686 % does not ground enumeration finished waitflag
687 grd_wf0(WF0),
688 ground_waitflags_store_clpfd(Store).
689 ground_inner_wait_flags(no_wf_available) :- !.
690 ground_inner_wait_flags(W) :-
691 add_internal_error('Inner waitflags in wrong format: ',ground_inner_wait_flags(W)).
692
693 % copy WF0 and WFE, set up a new store for the rest
694 copy_wf01e_wait_flags(wfx(WF0,WFX,WFE,Infos),wfx(WF0,S,WFE,Infos)) :-
695 init_wait_flags_store(S),
696 get_waitflag_from_store(1,copy_wf01e,WFX,WF1), % now ground wait flag 1 if it is grounded in the orginal WF store
697 copy_wf01e_wait_flags_aux(WF1,S).
698 copy_wf01e_wait_flags(no_wf_available,no_wf_available).
699
700 :- block copy_wf01e_wait_flags_aux(-,?).
701 copy_wf01e_wait_flags_aux(_,S) :- ground_waitflags_store_up_to_no_clpfd(1,S). % no CLPFD - labeling required until prio 1
702
703
704 clone_wait_flags_from1(wfx(_WF0,S,WFE,Infos),wfx(_NewWF0,S,WFE,Infos)).
705 clone_wait_flags_from1(no_wf_available,no_wf_available).
706 % creates a copy of the wait_flags, except for WF0; this needs to be grounded separately with grd_wf0/1
707 clone_wait_flags_from1_finish(wfx(WF0,_,_,_),wfx(WF0,_,_,_)). % finish copying by copying over WF0
708 clone_wait_flags_from1_finish(no_wf_available,no_wf_available).
709
710 get_wait_flag0(wfx(WF0,_,_,_),WF0).
711 get_wait_flag0(no_wf_available,0).
712
713 %get_wait_flag0(WFX,WF0) :- get_wait_flag(0,WFX,WF0). /* when this is ground you can do det. propagatons */
714 get_wait_flag1(WFX,WF1) :- get_wait_flag(1.0,WFX,WF1). /* when this is ground you can do det. propagatons */
715 get_wait_flag1(Info,WFX,WF1) :- get_wait_flag(1.0,Info,WFX,WF1).
716
717 get_binary_choice_wait_flag(Info,WFX,WF2) :- !, get_binary_choice_wait_flag_exp_backoff(4,Info,WFX,WF2).
718 %get_binary_choice_wait_flag(Info,WFX,WF2) :- % use 3 rather than 2 to give priority over enumeration choicepoints
719 % get_wait_flag(4,Info,WFX,WF2). /* when this is ground you can do binary propagatons */
720
721 get_middle_wait_flag(Info,WFX,WFn) :- get_wait_flag(10,Info,WFX,WFn).
722
723 get_last_wait_flag(Info,WFX,Res) :- last_finite_priority(P),get_wait_flag(P,Info,WFX,Res).
724
725 % ------------------------
726 get_wait_flag_infos(wfx(_,_,_,Infos),Infos).
727 get_wait_flag_infos(no_wf_available,[]).
728
729 :- use_module(library(ordsets),[ord_member/2,ord_add_element/3]).
730 get_wait_flag_info(wfx(_,_,_,Infos),Info) :- member(Info,Infos).
731
732 % use if the Info field is known, requires ordered/sorted Infos list
733 is_wait_flag_info(wfx(_,_,_,Infos),Info) :-
734 (nonvar(Infos) -> ord_member(Info,Infos) ; print(var_waitflag),nl,fail).
735
736 %set_wait_flag_infos(wfx(WF0,Store,WFE,_),Infos,wfx(WF0,Store,WFE,Infos)).
737 %set_wait_flag_infos(no_wf_available,_,no_wf_available).
738
739 add_wait_flag_info(wfx(WF0,Store,WFE,Infos),Info,wfx(WF0,Store,WFE,NewInfos)) :-
740 ord_add_element(Infos,Info,NewInfos). %, print(added_wf_infos(NewInfos)),nl.
741 add_wait_flag_info(no_wf_available,_,no_wf_available).
742
743 % ------------------------
744
745 % BINARY CHOICE WAITFLAGS
746
747 :- use_module(tools,[max_tagged_pow2/1]).
748 % last power of 2 exponent that is still a finite priority
749 last_finite_pow2_prio_exponent(Lim) :- tools:max_tagged_pow2(Pow),
750 Lim is Pow-1. % stay below last_finite_priority, i.e., max_tagged_integer-1024
751
752 % utility to get power of two priority:
753 get_pow2_binary_choice_priority(Exp,Prio) :- last_finite_pow2_prio_exponent(Lim),
754 (Exp =< Lim -> Prio is integer(2**Exp)
755 ; Prio is integer(2**Lim)).
756
757 % get a binary choice wait flag, start with 2 but for every further call double the priority (exponential backoff)
758 % idea: give priority to data enumerations when too many choice points arise
759 get_binary_choice_wait_flag_exp_backoff(Info,WFX,WF2) :-
760 get_binary_choice_wait_flag_exp_backoff(2,Info,WFX,WF2).
761 get_binary_choice_wait_flag_exp_backoff(_,_Info,no_wf_available,WF2) :- !, WF2=2.
762
763 % StartPrio should be a power of 2:
764 get_binary_choice_wait_flag_exp_backoff(StartPrio,Info,wfx(_,Store,_,_),WF2) :-
765 my_get_wf_store_atts(Store,Heap),
766 last_finite_pow2_prio_exponent(Lim),
767 get_bin_aux(StartPrio,Lim,Info,Heap,NewHeap,WF2),
768 put_atts(Store,+wf_store(NewHeap)).
769
770 % to do : maybe store attribute of current exponential ??
771 get_bin_aux(Prio,Lim,Info,Heap,NewHeap,WF2) :- avl_fetch(float(Prio),Heap,wf(WFs,OldInfo)),
772 !,
773 (Prio >= Lim -> WF2 = WFs, NewHeap=Heap /* for finite priorities single waitflag stored */
774 ; OldInfo = '$binary'(_) -> P2 is Prio*2, get_bin_aux(P2,Lim,Info,Heap,NewHeap,WF2)
775 ; otherwise -> WF2=WFs, avl_store(float(Prio),Heap,wf(WFs,'$binary'(Info)),NewHeap) % just update Info; so that next time we double
776 ).
777 get_bin_aux(Prio,_,Info,Heap,NewHeap,WF2) :- % we have found an unused power of 2
778 %print(get_bin(Prio,Info)),nl,
779 avl_store(float(Prio),Heap,wf(WF2,'$binary'(Info)),NewHeap).
780
781 % ------------------------
782
783 get_enumeration_finished_wait_flag(wfx(_,_,WFE,_),WFE) :- !.
784 get_enumeration_finished_wait_flag(no_wf_available,WFE) :- !,WFE=1.
785 get_enumeration_finished_wait_flag(W,E) :-
786 add_internal_error('Waitflags in wrong format: ',get_enumeration_finished_wait_flag(W,E)).
787
788
789 ?ground_det_wait_flag(wfx(WF0,Store,_,_)) :- !, %% print(ground_det_wait_flag),nl,
790 ? grd_wf0(WF0),
791 ground_waitflags_store_up_to_no_clpfd(1,Store). % no CLPFD labeling required for prio until 1
792 ground_det_wait_flag(no_wf_available) :- !.
793 ground_det_wait_flag(W) :-
794 add_internal_error('Waitflags in wrong format: ',ground_det_wait_flag(W)).
795
796 ?ground_wait_flag_to(wfx(WF0,Store,_,_),Limit) :- !,%% print(ground_det_wait_flag),nl,
797 ? grd_wf0(WF0),
798 ? ground_waitflags_store_clpfd_up_to(Limit,Store).
799 ground_wait_flag_to(no_wf_available,_) :- !.
800 ground_wait_flag_to(W,Limit) :-
801 add_internal_error('Waitflags in wrong format: ',ground_wait_flag_to(W,Limit)).
802
803 ground_waitflags_store_up_to_no_clpfd(Prio,Store) :- % print(prio(Prio)),nl,
804 my_get_wf_store_atts(Store,Heap), % portray_waitflags_store(Store), %
805 ( avl_min(Heap,Min,wf(WF,Info)) -> % heap is non-empty, first element is WF with priority Min
806 ( Min =< Prio -> % we have non-ground waitflags with a matching priority
807 (avl_del_min(Heap,AMin,_,NewHeap),AMin==Min % remove the first element
808 -> true
809 ; add_internal_error('Could not remove min. in ground_waitflags_store_up_to_no_clpfd: ',Min:Info:Heap), fail
810 ),
811 put_atts(Store,+wf_store(NewHeap)), % write the modified heap back
812 %% print(grounding2(Prio,_Info)),nl, %%
813 ground_prio(Min,WF,Info),
814 ground_waitflags_store_up_to_no_clpfd(Prio,Store) % continue recursively
815 ; /* otherwise -> */ % no more matching waitflags present
816 true) % stop here
817 ; /* otherwise -> */ % heap is empty, nothing more to do
818 true). % stop here
819
820 ground_waitflags_store_clpfd_up_to(Limit,Store) :-
821 ? my_get_wf_store_atts(Store,Heap),
822 ? my_get_fdvars_atts(Store,FDVars),!,
823 ? ( avl_del_min(Heap,Prio,wf(WF,Info),NewHeap) -> % we have non-ground waitflags with a matching priority
824 ? ( my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS),
825 fd_priority_leq_limit(NextFDVar,FDPrio,Limit)
826 ? -> (FDPrio > Limit -> true
827 ? ; enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS))
828 ? ; (Prio > Limit -> true
829 ? ; put_atts(Store,+wf_store(NewHeap)), % write the modified heap back
830 %print(grounding(Prio,_Info)),nl,
831 ? ground_prio(Prio,WF,Info), % ground waitflag
832 ? ground_waitflags_store_clpfd_up_to(Limit,Store) % continue recursively
833 )
834 )
835 ; ((my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,RemainingFDVARS),
836 fd_priority_leq_limit(NextFDVar,FDPrio,Limit))
837 -> enum_fd_variable_store(FDPrio,Store,NextFDVar,RemainingFDVARS)
838 ; true % heap and FDVars list is empty, nothing more to do
839 )
840 ).
841 ground_waitflags_store_clpfd_up_to(Limit,Store) :-
842 add_internal_error('Illegal Waitflag: ',ground_waitflags_store_clpfd_up_to(Limit,Store)),fail.
843
844
845 update_waitflag(Prio,CurrentWaitflag,NewWaitFlag,WF) :-
846 /* if the CurrentWaitflag is already ground and now a new WF with lower priority has been added to the store:
847 create a new non-ground waitflag to give priority to new WF with lower priority value */
848 (var(CurrentWaitflag) -> NewWaitFlag=CurrentWaitflag
849 ; WF=wfx(_WF0,Store,_,_),my_get_wf_store_atts(Store,Heap),
850 ((avl_min(Heap,Min,wf(_NewerWF,_Info)),(Prio=inf;Min<Prio))
851 -> get_waitflag_from_store(Prio,updated_wf,Store,NewWaitFlag) % could be optimized
852 % ,print(updating_wf(Prio,CurrentWaitflag,NewWaitFlag,Min)),nl
853 ; fdvar_with_higher_prio_exists(Store,Prio) -> get_waitflag_from_store(Prio,updated_wf,Store,NewWaitFlag)
854 ; NewWaitFlag=CurrentWaitflag
855 )
856 ).
857
858 fdvar_with_higher_prio_exists(Store,Limit) :-
859 my_get_fdvars_atts(Store,FDVars),
860 my_get_next_fdvar_to_enumerate(FDVars,NextFDVar,_),
861 fd_priority_leq_limit(NextFDVar,_,Limit).
862
863 % get the minimum waitflag, if it exists
864 get_minimum_waitflag_prio(wfx(_,Store,_,_),MinPrio,Info) :-
865 my_get_wf_store_atts(Store,Heap),
866 avl_min(Heap,MinPrio,wf(_LWF,Info)).
867
868
869 % currently not used:
870 %/* same as get_enumeration_finished_wait_flag, but will fail if user does not want
871 % to look for abort errors */
872 %get_abort_error_flag(wfx(_,_,WFE,_),WFE) :- !, \+ preferences:preference(find_abort_values,false).
873 %get_abort_error_flag(W,_) :-
874 % add_internal_error(get_abor_error_flag,'Waitflags in wrong format: ',W).
875
876
877 add_wd_error(Msg,Term,WF) :- add_wd_error_span(Msg,Term,unknown,WF).
878 add_wd_error_set_result(Msg,Term,Result,ResultValue,Span,WF) :-
879 is_wd_guarded_result(Result), % just dummy co-routine to detect variables which have WD assignments pending on them; should not be enumerated
880 % add well-definedness error but also set Result Variable to ResultValue to trigger pending co-routines if wd_guarded
881 add_abort_error7(well_definedness_error,Msg,Term,Result,ResultValue,Span,WF).
882 add_wd_error_span(Msg,Term,Span,WF) :- add_abort_error_span(well_definedness_error,Msg,Term,Span,WF).
883
884 :- block is_wd_guarded_result(-).
885 is_wd_guarded_result(_).
886
887 add_abort_error_span(ErrType,Msg,Term,Span,WF) :- add_abort_error7(ErrType,Msg,Term,0,0,Span,WF).
888
889 :- use_module(tools_printing,[print_error/1]).
890 add_abort_error7(_ErrType,_Msg,_Term,_Result,_ResultValue,_Span,_WF) :-
891 preference(disprover_mode,true),
892 !, % When Disproving we can assume well-definedness of the PO; sometimes the relevant hypotheses will be filtered out
893 fail.
894 add_abort_error7(ErrType,Msg,Term,Result,ResultValue,Span,WF) :-
895 preference(raise_abort_immediately,F), F \= false,
896 !,
897 (F=full -> AWF=1 % really raise immediately; may entail more spurious messages, particularly in WF0
898 ; get_idle_wait_flag(add_abort_error7,WF,AWF), %get_wait_flag0(WF,AWF),
899 mark_pending_abort_error(WF,Msg,Term,Span)
900 ),
901 %when(nonvar(AWF),
902
903 %(print_error('Raising error immediately without checking rest of predicate for unsatisfiability (because RAISE_ABORT_IMMEDIATELY set to TRUE): error could be spurious!'),true)),
904 string_concatenate('Raising error immediately without checking rest of predicate for unsatisfiability (because RAISE_ABORT_IMMEDIATELY set to TRUE): error could be spurious!~n! ',Msg,NewMsg),
905 add_abort_error2(AWF,ErrType,NewMsg,Term,Result,ResultValue,Span).
906 add_abort_error7(ErrType,Msg,Term,Result,ResultValue,Span,WF) :-
907 %nl,print(possible_wd_error(WF,Msg)),nl,
908 mark_pending_abort_error(WF),
909 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:
910 %integer_priority(P), get_wait_flag(P,add_abort_error7,WF,AWF), % causes problem for test 1122
911 add_abort_error2(AWF,ErrType,Msg,Term,Result,ResultValue,Span).
912
913
914 % no longer used:
915 %add_wd_error_span_now(Msg,Term,Span) :- add_abort_error2(true,well_definedness_error,Msg,Term,0,0,Span).
916
917 :- use_module(state_space,
918 [store_abort_error_for_context_state_if_possible/4]).
919 :- block add_abort_error2(-,?,?,?,?,?,?).
920 add_abort_error2(wd_guarded,_Err,_Msg,_Term,Result,ResultValue,_Span) :- !,% ignoring error message; no-wd problem
921 % set Result to default ResultValue; useful to get rid of pending co-routines; result will not be used
922 (Result=ResultValue -> true ; print(add_abort_error_failure(Result,ResultValue)),nl).
923 add_abort_error2(_AWF,ErrType,Msg,Term,_,_,Span) :- % print(adding_abort_error(Msg)),nl,trace,
924 % add_error(abort_error,Msg,Term),
925 (store_abort_error_for_context_state_if_possible(ErrType,Msg,Term,Span)
926 -> true
927 ; add_error(add_abort_error2,'Could not store error: ',(Msg,Term),Span)),
928 % print(done),nl,
929 fail.
930
931 /*
932 ground_wait_flags_catch_enumeration_warnings(WF) :-
933 on_exception(enumeration_warning(A,B,C,D,E),
934 ground_wait_flags(WF),
935 (pending_abort_error(WF,Msg,Term,Span),
936 format('WD-Error may be causing Enumeration Warning : ~w~n',[Msg]),
937 fail
938 ;
939 throw(enumeration_warning(A,B,C,D,E)))).
940 */
941
942 % check whether an abort error is pending.
943 pending_abort_error(wfx(_WF0,_WFX,WFE,_)) :- var(WFE),
944 get_atts(WFE,+wf_abort_pending).
945 % succeed once for every pending abort error in Waitflag store
946 pending_abort_error(wfx(_WF0,_WFX,WFE,_),Msg,Term,Span) :-
947 frozen(WFE,Goal), %print(goal(Goal)),nl,
948 pending_abort_error_aux(Goal,Msg,Term,Span).
949
950 pending_abort_error_aux(kernel_waitflags:add_abort_error2(_,_ErrType,Msg,Term,_Result,_ResultValue,Span), Msg,Term,Span).
951 pending_abort_error_aux(add_abort_error2(_,_ErrType,Msg,Term,_Result,_ResultValue,Span), Msg,Term,Span).
952 pending_abort_error_aux(kernel_waitflags:mark_pending_abort_error2(_,Msg,Term,Span), Msg,Term,Span).
953 pending_abort_error_aux((A,B),Msg,Term,Span) :-
954 (pending_abort_error_aux(A,Msg,Term,Span) ;
955 pending_abort_error_aux(B,Msg,Term,Span) ).
956
957 % explicitly mark that an abort error is pending
958 mark_pending_abort_error(wfx(_WF0,_WFX,WFE,_)) :-
959 (var(WFE) -> put_atts(WFE,+wf_abort_pending)
960 ; true).
961 % explicitly mark that an abort error is pending with storing information
962 mark_pending_abort_error(wfx(_WF0,_WFX,WFE,_),Msg,Term,Span) :-
963 (var(WFE) -> put_atts(WFE,+wf_abort_pending),
964 mark_pending_abort_error2(WFE,Msg,Term,Span)
965 ; true).
966 mark_pending_abort_error(no_wf_available,_Msg,_Term,_Span).
967
968 :- block mark_pending_abort_error2(-,?,?,?).
969 mark_pending_abort_error2(_,_,_,_).
970 % ---------------------------------------------
971
972 ground_wait_flag_to_enumeration(WF) :-
973 ? last_finite_priority(P),
974 ? ground_wait_flag_to(WF,P).
975
976 get_large_finite_wait_flag(Info,WFX,WF2) :-
977 large_finite_priority(P),
978 get_wait_flag(P,Info,WFX,WF2).
979
980 get_enumeration_starting_wait_flag(Info,WFX,WF2) :-
981 last_finite_priority(P),
982 get_wait_flag(P,enumeration_starting(Info),WFX,WF2).
983
984 get_inner_enumeration_over_wait_flag(WFX,Info,WF2) :-
985 tools:max_tagged_integer(P), % a bit of a hack; what if inf_type_prio generates something higher ? TO DO : bound get_max_cardinality_as_priority/inf_type_prio result
986 get_wait_flag(P,inner_enumeration(Info),WFX,WF2).
987
988 get_integer_enumeration_wait_flag(Info,WFX,WF2) :-
989 integer_priority(Prio),
990 get_wait_flag(Prio,Info,WFX,WF2).
991
992 % A few hardcoded priorities
993 large_finite_priority(P) :- integer_priority(I), P is I-1000.
994 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
995 %integer_priority(10000000). % old value
996 integer_priority(Prio) :- % priority for X : INTEGER enumerations
997 tools:max_tagged_integer(P),
998 Prio is P - 1024.
999 % note inf_type_prio sets some priorities such as seq(INTEGER) ... higher
1000
1001 % priority for sets of infinite type (POW(INTEGER)...)
1002 integer_pow2_priority(Prio) :- % was 10000010
1003 integer_priority(P), Prio is P+10.
1004
1005
1006 is_finite_priority(P) :-
1007 P < 100000, % TO DO: put max_tagged_integer for 32-bit system minus 1026 or so (see above)
1008 last_finite_priority(LFP),
1009 P =< LFP.
1010
1011 %integer_pow_priority(10000020) :- tools:max_tagged_integer(MAX).
1012
1013 % ensure that we do not exceed priority of type enumeration or use too big numbers
1014 get_bounded_wait_flag(Prio,Info,WF,LWF) :-
1015 bound_prio(Prio,P),
1016 get_wait_flag(P,Info,WF,LWF).
1017
1018 bound_prio(Prio,P) :- number(Prio),
1019 Prio>268435452, % 268435455 is max_tagged_integer on 32 bit platforms
1020 last_finite_priority(MAX),
1021 Prio>MAX,
1022 !, P=MAX.
1023 bound_prio(P,P).
1024
1025 % -------------
1026
1027 % copy waitflags from one waitflag store to another; warning: can ground WF0 from outer store
1028 copy_waitflag_store(wfx(WF0,Store1,WFE1,_),wfx(WF0,Store2,WFE2,Info2)) :-
1029 %print(copy_wf(WF0,Store1,Store2,WFE1,WFE2)),nl,
1030 my_get_fdvars_atts(Store1,FDList1),
1031 (FDList1 = [] -> true
1032 ; my_get_fdvars_atts(Store2,FDList2),
1033 append(FDList1,FDList2,NewFDList),
1034 put_atts(Store2,+wf_fdvars(NewFDList))),
1035 my_get_wf_store_atts(Store1,Heap),
1036 avl_to_list(Heap,WF_List),
1037 %print(copying(FDList1,WF_List)),nl,
1038 maplist(add_wf(wfx(WF0,Store2,WFE2,Info2)),WF_List), % we could do this faster
1039 clear_store(Store1), Store1=Store2, % ensure that calls which add waitflags now get-redirected to waitflag store 2
1040 %portray_waitflags(wfx(WF0,Store2,WFE2,Info2)),
1041 WFE1=WFE2.
1042
1043 clear_store(Store) :- put_atts(Store,-wf_store(_)), put_atts(Store,-wf_fdvars(_)).
1044
1045 add_wf(WF,Prio-wf(WFlag,Info)) :- % purpose: copy waitflag WFlag to WF
1046 (Prio=float(P) -> true ; P=Prio),
1047 (var(WFlag) -> get_wait_flag(P,Info,WF,WFlag) % we just have a single waitflag
1048 ; maplist(get_wait_flag(P,Info,WF),WFlag)). % we have a list of waitflags
1049
1050
1051 % try and find an exact match for a waitflag info; this is linear in size of waitflag store !
1052 find_waitflag_info(wfx(_WF0,Store,_WFE1,_),Info,Prio,WF1) :-
1053 my_get_wf_store_atts(Store,Heap),
1054 avl_member(Prio,Heap,wf(WF1,Info)).
1055 % --------------------------
1056 % DEBUGGING UTILITIES:
1057
1058 % you also need to comment in code above using it
1059
1060 :- dynamic debug_kernel_waitflags/1.
1061 set_debug_kernel_waitflags :- (debug_kernel_waitflags(_) -> true ; assert(debug_kernel_waitflags(0))).
1062
1063 % the counter allows to set trace points
1064 debug_kernel_waitflags(Counter) :- retract(debug_kernel_waitflags(C)), C1 is C+1,
1065 assert(debug_kernel_waitflags(C1)), Counter=C1.
1066
1067 get_debug_info(Info,Res) :- debug_kernel_waitflags(Counter),!,
1068 (Counter==39 -> trace ; true),
1069 Res=info(Counter,Info).
1070 get_debug_info(Info,Info).
1071
1072 % --------------------------
1073
1074 :- assert_must_succeed(( kernel_waitflags:test_waitflags(1) )).
1075 :- assert_must_succeed(( kernel_waitflags:test_waitflags(2) )).
1076
1077 test_waitflags(1) :-
1078 init_wait_flags(Waitflags),
1079 get_wait_flag(10,Waitflags,WF1),
1080 when(nonvar(WF1),print('WF1 - Prio 10\n')),
1081 get_wait_flag(5,Waitflags,WF2),
1082 when(nonvar(WF2),(print('WF2 - Prio 5\n'),
1083 get_wait_flag(6,Waitflags,WF4),
1084 when(nonvar(WF4),print('WF4 - Prio 6\n')),
1085 get_wait_flag(2,Waitflags,WF5),
1086 when(nonvar(WF5),print('WF5 - Prio2\n')))),
1087 get_wait_flag(5,Waitflags,WF3),
1088 when(nonvar(WF3),print('WF3 - Prio 5\n')),
1089 ground_wait_flags(Waitflags),nl.
1090
1091 test_waitflags(2) :-
1092 init_wait_flags(Waitflags),
1093 get_wait_flag(10,Waitflags,WF1),
1094 when(nonvar(WF1),print('WF1 Prio 10\n')),
1095 get_wait_flag(5,Waitflags,WF2),
1096 when(nonvar(WF2),(print('WF2 - Prio 5\n'),
1097 get_wait_flag(6,Waitflags,WF4),
1098 when(nonvar(WF4),print('WF4 - Prio 6\n')),
1099 get_wait_flag(2,Waitflags,WF5),
1100 when(nonvar(WF5),print('WF5 - Prio2\n')))),
1101 get_wait_flag(5,Waitflags,WF3),
1102 when(nonvar(WF3),print('WF3 - Prio 5\n')),
1103 ground_wait_flags(Waitflags),nl.