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