1 | | % (c) 2009-2024 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 | | :- module(b_interpreter_components,[ |
6 | | reset_component_info/1, |
7 | | b_trace_test_components/3, b_trace_test_components/4, |
8 | | b_trace_test_components_wf/4, |
9 | | unsat_component/2, unsat_component_enumeration_warning/1, |
10 | | unsat_or_unknown_component_exists/0, |
11 | | unsat_component_abort_error/1, |
12 | | unsat_conjunct_inside_component/4, |
13 | | det_solution_for_constant/2, get_det_solution_for_constant_value_kind/2, |
14 | | det_solution_for_constant_was_stored/0, det_solution_for_constant_was_stored/1, |
15 | | b_tracetest_boolean_expression/5, |
16 | | observe_state/3, |
17 | | |
18 | | construct_optimized_exists/3, construct_optimized_exists/4, |
19 | | construct_optimized_exists/5, |
20 | | extract_equalities_in_quantifier/4 |
21 | | ]). |
22 | | |
23 | | :- use_module(b_interpreter,[b_test_boolean_expression/6]). |
24 | | :- use_module(preferences). |
25 | | :- use_module(kernel_waitflags). |
26 | | :- use_module(tools). |
27 | | :- use_module(translate). |
28 | | :- use_module(error_manager). |
29 | | :- use_module(library(lists)). |
30 | | :- use_module(b_enumerate, [b_tighter_enumerate_all_values/2, b_tighter_enumerate_values/3]). |
31 | | |
32 | | :- use_module(debug,[debug_println/2, debug_mode/1]). |
33 | | :- use_module(tools,[start_ms_timer/1, stop_ms_timer_with_msg/2, stop_ms_timer_with_debug_msg/2]). |
34 | | |
35 | | :- use_module(chrsrc(chr_integer_inequality),[attach_chr_integer_inequality_to_wf_store/1]). |
36 | | |
37 | | |
38 | | :- use_module(module_information,[module_info/2]). |
39 | | :- module_info(group,interpreter). |
40 | | :- module_info(description,'This module can be used to split a predicate into components and solving each component individually (with tracing feedback).'). |
41 | | |
42 | | /* -------------- */ |
43 | | /* dynamic bits */ |
44 | | /* -------------- */ |
45 | | |
46 | | :- dynamic skipped_unsat_conjunct/1, unsat_conjunct_inside_component/4. |
47 | | reset_unsat_conjunct_inside_component_info :- |
48 | | retractall(skipped_unsat_conjunct(_)), |
49 | | retractall(unsat_conjunct_inside_component(_,_,_,_)). |
50 | | reset_tracetest_for_component :- retractall(skipped_unsat_conjunct(_)). % to be called before trace checking a component |
51 | | |
52 | | % individual conjuncts which are directly unsatisfiable |
53 | | assert_skipped_failed_conjunct(ComponentNr,BE) :- assert_skipped_unsat_conjunct, |
54 | | assertz(unsat_conjunct_inside_component(ComponentNr,BE,false,'')), |
55 | | (preferences:get_preference(provide_trace_information,false) -> true |
56 | | ; add_message(b_interpreter_components,' *** INCONSISTENCY DETECTED, SKIPPING COMPONENT NR: ',ComponentNr, BE), |
57 | | (debug_mode(on) -> print_bexpr_with_limit(BE,500) ; print_bexpr_with_limit(BE,150)), nl |
58 | | ). |
59 | | assert_skipped_unknown_conjunct(ComponentNr,BE,Reason) :- assert_skipped_unknown_conjunct, |
60 | | assertz(unsat_conjunct_inside_component(ComponentNr,BE,unknown,Reason)), |
61 | | ((Reason=abort_error; Reason=well_definedness_error) |
62 | | -> assert_unsat_component_abort_error(ComponentNr) |
63 | | ; true), |
64 | | (preferences:get_preference(provide_trace_information,false) -> true |
65 | | ; get_reason_msg(Reason,MSG),format(' *** Skipping predicate due to ~w:',[MSG]), |
66 | | write(' '),print_bexpr_with_limit(BE,150),nl, |
67 | | format('~n *** Predicate is unknown~n *** COMPONENT NUMBER: ~w~n',[ComponentNr]) |
68 | | ). |
69 | | assert_skipped_unsat_conjunct :- |
70 | | (skipped_unsat_conjunct(false) -> true |
71 | | ; retractall(skipped_unsat_conjunct(_)), assertz(skipped_unsat_conjunct(false))). |
72 | | assert_skipped_unknown_conjunct :- |
73 | | (skipped_unsat_conjunct(_) -> true ; assertz(skipped_unsat_conjunct(unknown))). |
74 | | |
75 | | get_reason_msg(enumeration_warning,R) :- !, R='ENUMERATION WARNING'. |
76 | | get_reason_msg(M,M). |
77 | | |
78 | | |
79 | | :- dynamic unsat_component/2, unsat_component_enumeration_warning/1, unsat_component_abort_error/1. |
80 | | reset_unsat_component_info :- |
81 | | retractall(unsat_component(_,_)), |
82 | | retractall(unsat_component_enumeration_warning(_)), |
83 | | retractall(unsat_component_abort_error(_)). |
84 | | |
85 | | reset_all_component_infos :- |
86 | | reset_unsat_component_info, |
87 | | reset_unsat_conjunct_inside_component_info, |
88 | | reset_observe_state. |
89 | | |
90 | | :- use_module(eventhandling,[register_event_listener/3]). |
91 | | :- register_event_listener(clear_specification,reset_all_component_infos, |
92 | | 'Reset b_interpreter_components unsat component info.'). |
93 | | |
94 | | |
95 | | store_unsat_component_nr(X) :- % use if you do not know if it is unknown or unsat |
96 | | %error_manager:print_error_scopes, |
97 | ? | (critical_enumeration_warning_occured_in_error_scope |
98 | | -> Res = unknown |
99 | | % should we also detect abort_error_occured_in_error_scope ? |
100 | | ; Res=false), |
101 | | store_unsat_component_nr(X,Res). |
102 | | |
103 | | store_unsat_component_nr(X,FalseOrUnknown) :- |
104 | | debug_println(9,store_unsat_component_nr(X,FalseOrUnknown)), |
105 | | (unsat_component(X,false) -> true |
106 | | ; retractall(unsat_component(X,_)),assertz(unsat_component(X,FalseOrUnknown)) |
107 | | ), |
108 | ? | (critical_enumeration_warning_occured_in_error_scope, |
109 | | \+ unsat_component_enumeration_warning(X) |
110 | | -> assertz(unsat_component_enumeration_warning(X)), |
111 | | debug_println(19,enumeration_warning_in_component(X)) |
112 | | ; true), |
113 | | (abort_error_occured_in_error_scope |
114 | | -> assert_unsat_component_abort_error(X) |
115 | | ; true). |
116 | | %portray_unsat_components. |
117 | ? | unsat_component_exists :- unsat_component(_,false),!. |
118 | | unsat_or_unknown_component_exists :- unsat_component(_,_),!. |
119 | | |
120 | | % store the fact that an abort or wd error occurred for component |
121 | | assert_unsat_component_abort_error(X) :- \+ unsat_component_abort_error(X), |
122 | | assertz(unsat_component_abort_error(X)), |
123 | | debug_println(19,unsat_component_abort_error(X)). |
124 | | |
125 | | % small utility to display status of components |
126 | | :- public portray_unsat_components/0. |
127 | | portray_unsat_components :- format('Unsat components~n',[]), |
128 | | unsat_component(Comp,Status), |
129 | | (unsat_component_abort_error(Comp) -> Abort='abort/well_definedness error' ; Abort=''), |
130 | | (unsat_component_enumeration_warning(Comp) -> EW='enumeration_warning' ; EW=''), |
131 | | format('* component: ~w -> ~w ~w ~w~n',[Comp,Status,Abort,EW]), |
132 | | unsat_conjunct_inside_component(Comp,BE,S,Reason), |
133 | | format(' -> reason for ~w: ~w : ',[S,Reason]), translate:print_bexpr_with_limit(BE,100),nl, |
134 | | fail. |
135 | | portray_unsat_components. |
136 | | |
137 | | :- public nr_of_components/1. |
138 | | :- dynamic nr_of_components/1. |
139 | | :- dynamic allow_skipping_over_components/0. |
140 | | reset_component_info(AllowSkip) :- retractall(allow_skipping_over_components), |
141 | | (AllowSkip==true -> assertz(allow_skipping_over_components) ; true), |
142 | | reset_unsat_component_info, |
143 | | reset_unsat_conjunct_inside_component_info. |
144 | | |
145 | | % in Disprover mode we can stop straightaway; we are not interested in finding values for identifiers in other components; unless we have an enumeration warning !! |
146 | | % TO DO: keep track of enumeration warnings per component |
147 | | |
148 | | /* -------------- */ |
149 | | |
150 | | |
151 | | :- use_module(library(avl)). |
152 | | % You can use the term one as ComponentNr if you don't know the number or it is not relevant |
153 | | b_tracetest_boolean_expression(BE,LS,S,WF,ComponentNr) :- %print(catch(ComponentNr)),nl, |
154 | | empty_avl(Ai), |
155 | ? | catch_enumeration_warning_exceptions_and_wd_failures( |
156 | | b_tracetest_boolean_expression1(BE,LS,S,WF,ComponentNr,Ai,_Ao), |
157 | | % this should not backtrack (without WF0 set); otherwise we may get problems with error blocks nesting |
158 | | assert_skipped_unknown_conjunct(ComponentNr,BE,enumeration_warning), |
159 | | true, % true: add exceptions as event errors |
160 | | assert_skipped_unknown_conjunct(ComponentNr,BE,well_definedness_error), % for WD error |
161 | | component(ComponentNr)). % Source argument for debugging |
162 | | |
163 | | b_tracetest_boolean_expression1(b(Expr,_,Infos),LS,S,WF,ComponentNr,Ai,Ao) :- |
164 | ? | b_tracetest_boolean_expression2(Expr,Infos,LS,S,WF,ComponentNr,Ai,Ao). |
165 | | |
166 | | :- use_module(bsyntaxtree,[is_a_conjunct_or_neg_disj/3]). |
167 | | b_tracetest_boolean_expression2(Conjunct,Info,LocalState,State,WF,ComponentNr,Ai,Ao) :- |
168 | | is_a_conjunct_or_neg_disj(b(Conjunct,pred,Info),LHS,RHS), |
169 | | % also detects: not(A or B) <=> not(A) & not(B) and not(A=>B) <=> A & not(B) |
170 | | % \+(Info = [try_smt|_]), |
171 | | !, |
172 | | %% print('check lhs of conjunct: '), translate:print_bexpr(LHS),nl, |
173 | ? | b_tracetest_boolean_expression1(LHS,LocalState,State,WF,ComponentNr,Ai,Aii), |
174 | | %% print('check rhs of conjunct: '), translate:print_bexpr(RHS),nl, |
175 | ? | b_tracetest_boolean_expression1(RHS,LocalState,State,WF,ComponentNr,Aii,Ao). |
176 | | b_tracetest_boolean_expression2(BE,Infos,LS,S,WF,ComponentNr,Ai,Ao) :- |
177 | | (get_preference(provide_trace_information,true) |
178 | | -> format('~n ====> (~w) ',[ComponentNr]), |
179 | | print_bexpr_with_limit(b(BE,pred,Infos),250),nl |
180 | | ; true), |
181 | ? | if(safe_b_test_boolean_expression(ComponentNr,b(BE,pred,Infos),LS,S,WF,Ai,Ao), |
182 | | true, |
183 | | (assert_skipped_failed_conjunct(ComponentNr,b(BE,pred,Infos)), |
184 | | Ai=Ao)). |
185 | | |
186 | | |
187 | | safe_b_test_boolean_expression(ComponentNr,BE,LS,S,WF,Ai,Ao) :- %print(c(_ComponentNr,WF)),nl, translate:print_bexpr(BE),nl, |
188 | | preferences:get_preference(provide_trace_information,false), |
189 | | \+ abort_error_occured_in_error_scope, |
190 | | % avoid setting up new error scope for every conjunct with catch_enumeration_warning |
191 | | % BE is only displayed in TRACE_INFO mode anyway |
192 | | % can be a bottleneck, e.g., for :prob-file ../prob_examples/public_examples/Eval/Sudoku_SAT.eval |
193 | | !, |
194 | ? | if(b_test_boolean_expression(BE,LS,S,WF,Ai,Ao),true, |
195 | | (abort_error_occured_in_error_scope |
196 | | -> assert_unknown(ComponentNr,BE,abort_error,Ai,Ao) |
197 | | ; fail) |
198 | | ). |
199 | | safe_b_test_boolean_expression(ComponentNr,BE,LS,S,WF,Ai,Ao) :- |
200 | ? | catch_enumeration_warning_exceptions_and_wd_failures( |
201 | | b_test_boolean_expression(BE,LS,S,WF,Ai,Ao), % this should not backtrack (without WF0 set); |
202 | | % otherwise we may get problems with error blocks nesting |
203 | | assert_unknown(ComponentNr,BE,enumeration_warning,Ai,Ao), % call when exception caught |
204 | | true, % true: add exceptions as event errors |
205 | | assert_unknown(ComponentNr,BE,well_definedness_error,Ai,Ao), % call when wd_error caught |
206 | | component(ComponentNr)). % Source argument for debugging |
207 | | |
208 | | % Note: this will only detect enumeration warnings and WD errors in WF0 phase |
209 | | |
210 | | assert_unknown(ComponentNr,BE,Reason,Ai,Ao) :- % print(assert_unknown(ComponentNr,Reason)),nl, |
211 | | assert_skipped_unknown_conjunct(ComponentNr,BE,Reason),Ai=Ao. |
212 | | |
213 | | :- use_module(bsyntaxtree,[predicate_components_in_scope/3, |
214 | | predicate_identifiers_in_scope/3]). |
215 | | :- use_module(b_enumerate,[extract_do_not_enum_ids_from_predicate/2]). |
216 | | |
217 | | get_typedval_id(typedval(_Val,_Type,ID,_),ID). |
218 | | b_trace_test_components(Properties,ConstantsState,TypedVals) :- |
219 | | %format('b_trace_test_components(~w,~w,~w)',[Properties,ConstantsState,TypedVals]), |
220 | ? | b_trace_test_components(Properties,ConstantsState,TypedVals,all). |
221 | | b_trace_test_components_wf(Properties,ConstantsState,TypedVals,WF) :- |
222 | ? | b_trace_test_components_wf(Properties,ConstantsState,TypedVals,all,[],WF). |
223 | | b_trace_test_components(Properties,ConstantsState,TypedVals,RelevantVariables) :- |
224 | ? | b_trace_test_components(Properties,ConstantsState,TypedVals,RelevantVariables,[]). |
225 | | % in constract to /3 and /4 b_trace_test_components/5 does not fail if the predicate is unsatisfiable. |
226 | | % instead it returns the unsatisfiable component as the last argument |
227 | | % the other predicates still fail because they expect to find an empty list there |
228 | | b_trace_test_components(Properties,ConstantsState,TypedVals,RelevantVariables,UnsatConjuncts) :- |
229 | ? | b_trace_test_components_wf(Properties,ConstantsState,TypedVals,RelevantVariables,UnsatConjuncts,no_wf_available). |
230 | | b_trace_test_components_wf(Properties,ConstantsState,TypedVals,RelevantVariables,UnsatConjuncts,OuterWF) :- |
231 | | % we now treat all TypedVals as local variables: |
232 | | % ensure that we count them in predicate_components/predicate_identifiers |
233 | | % (in case they clash with global identifiers)! |
234 | | %start_ms_timer(T0), |
235 | | maplist(get_typedval_id,TypedVals,LocalVars), |
236 | | extract_do_not_enum_ids_from_predicate(Properties,DoNotEnumVars), |
237 | | (preferences:get_preference(partition_predicates,true) |
238 | | -> predicate_components_in_scope(Properties,LocalVars,Components), |
239 | | length(Components,Len), print_trace_message(nr_of_components(Len)) |
240 | | ; predicate_identifiers_in_scope(Properties,LocalVars,PropVars), |
241 | | Components = [component(Properties,PropVars)], Len=1 |
242 | | ), |
243 | | retractall(nr_of_components(_)),assertz(nr_of_components(Len)), |
244 | | reset_observe_state, |
245 | | %stop_ms_timer_with_debug_msg(T0,b_trace_test_components_preprocessing), |
246 | ? | b_trace_test_components2(Components,ConstantsState,TypedVals,RelevantVariables,DoNotEnumVars,1,WFList,OuterWF), |
247 | ? | get_unsat_component(WFList,Components,UnsatConjuncts). |
248 | | |
249 | | |
250 | | get_unsat_component(_,Components,UnsatConjuncts) :- |
251 | | unsat_component_exists, !, |
252 | | debug_println(9,unsatisfiable_predicate), |
253 | ? | unsat_component(UnsatComponentNr,false),!, |
254 | | nth1(UnsatComponentNr,Components,component(UnsatConjuncts,_)). |
255 | | get_unsat_component(WFList,Components,UnsatConjuncts) :- |
256 | ? | get_unsat_component_aux(WFList,Components,UnsatConjuncts). |
257 | | |
258 | | get_unsat_component_aux(WFList,Components,UnsatConjuncts) :- |
259 | | print_trace_message(grounding_wait_flags), % Note: for unknown components we continue grounding |
260 | ? | l_ground_wait_flags(WFList), |
261 | | (unsat_component(UnsatComponentNr,Status) |
262 | | -> debug_println(19,unsat_component(UnsatComponentNr,Status)), UnsatConjuncts \== [], |
263 | | nth1(UnsatComponentNr,Components,component(UnsatConjuncts,_)) |
264 | | ; UnsatConjuncts=[]). |
265 | | get_unsat_component_aux(_,Components,UnsatConjuncts) :- |
266 | | unsat_component(UnsatComponentNr,_),!, |
267 | | nth1(UnsatComponentNr,Components,component(UnsatConjuncts,_)). |
268 | | |
269 | | |
270 | | :- use_module(b_ast_cleanup,[predicate_level_optimizations/2]). |
271 | | :- use_module(debug,[printsilent_message/1]). |
272 | | print_trace_message(Msg) :- |
273 | | (preference(provide_trace_information,false) -> true |
274 | | ; printsilent_message(Msg)). |
275 | | print_trace_message_with_typed_vals(Msg,TypedVals) :- |
276 | | (preference(provide_trace_information,false) -> true |
277 | | ; printsilent_message(Msg), |
278 | | maplist(get_typedval_id,TypedVals,IDs), |
279 | | printsilent_message(IDs) |
280 | | ). |
281 | | |
282 | | b_trace_test_components2([],_ConstantsState,TypedVals,_,_,Nr,ResWF,OuterWF) :- |
283 | | (TypedVals=[] |
284 | | -> print_trace_message(finished_processing_component_predicates), |
285 | | ResWF=[] |
286 | | ; print_trace_message_with_typed_vals('Enumerating Identifiers without constraints: ',TypedVals), |
287 | | init_wait_flags(WF0,[b_tighter_enumerate_values]), |
288 | | copy_wait_flag_call_stack_info(OuterWF,WF0,WF), |
289 | | b_tighter_enumerate_all_values(TypedVals,WF), ResWF=[wf_comp(WF,Nr)]). |
290 | | b_trace_test_components2([component(Pred,ComponentVars)|T],ConstantsState,TypedVals,RelevantVariables,DoNotEnumVars, |
291 | | Nr,ResWF,OuterWF) :- |
292 | | N1 is Nr+1, |
293 | | ( RelevantVariables\=all, ComponentVars\= all, |
294 | | lists_are_disjoint(RelevantVariables,ComponentVars) |
295 | | -> print_trace_message(skipping_irrelevant_component(Nr,ComponentVars)), |
296 | | b_trace_test_components2(T,ConstantsState,TypedVals,RelevantVariables,DoNotEnumVars,N1,ResWF,OuterWF) |
297 | | ; %% print(checking_component_properties(Nr,ComponentVars)),nl, %% |
298 | | %% print(component(Nr)),nl, translate:nested_print_bexpr_as_classicalb(Pred),debug:nl_time, |
299 | | ResWF = [wf_comp(WF,Nr)|TWF], |
300 | | init_wait_flags(WF0,[wf_comp(Nr)]), |
301 | | copy_wait_flag_call_stack_info(OuterWF,WF0,WF), |
302 | | (preferences:preference(use_chr_solver,true) -> attach_chr_integer_inequality_to_wf_store(WF) ; true), |
303 | | (ComponentVars=all -> InTypedVals=TypedVals, OutTypedVals=[], ProjectedState=ConstantsState |
304 | | ; project_typed_vals(TypedVals,ComponentVars,InTypedVals,OutTypedVals), |
305 | | project_state(ConstantsState,ComponentVars,ProjectedState) % relevant, e.g., for exists which may wait on state becoming ground |
306 | | ), |
307 | | observe_state(InTypedVals,WF,Nr), %% |
308 | | reset_tracetest_for_component, |
309 | | predicate_level_optimizations(Pred,OPred), |
310 | | %%nl,nl,print(component(Nr,ComponentVars,ProjectedState)),nl,nl,translate:print_bexpr(Pred),nl,nl, |
311 | | %%start_ms_timer(T1), |
312 | ? | b_tracetest_boolean_expression(OPred,[],ProjectedState,WF,Nr), |
313 | | % Skipping can only occur in this call above, not in enumeration phase |
314 | | %% stop_ms_timer_with_debug_msg(T1,b_tracetest_boolean_expression(Nr)), |
315 | | %print(done_comp(Nr,WF,ComponentVars)),nl, |
316 | | (skipped_unsat_conjunct(FalseOrUnknown) |
317 | | -> store_unsat_component_nr(Nr,FalseOrUnknown), % can be unknown due to an enumeration warning, cf. test 1390 |
318 | | %print('UNSAT: '),translate:nested_print_bexpr(Pred),nl,nl, |
319 | | allow_skipping_over_components % check if we allow skipping over unsat components to |
320 | | % maybe find as many values for constants as possible (partial setup_constants),... |
321 | | % Note: in Disprover mode or solve_predicate (REPL) we can stop and fail straightaway |
322 | | ; %print_bt_message(setup_enumerating_constants(Nr)), %%(TypedVals)), %% |
323 | | b_tighter_enumerate_values(InTypedVals,DoNotEnumVars,WF), |
324 | | (preferences:get_preference(disprover_mode,false) -> true |
325 | ? | ; ground_wait_flag0(WF) % ensure that we give each component the chance to run WF0 inconsistency checks |
326 | | %, print(finished_wf0_grounding_in_disprover_mode(Nr)),nl |
327 | | ) |
328 | | ), |
329 | | % we could ground waitflag 1 (deterministic computations) ? |
330 | ? | b_trace_test_components2(T,ConstantsState,OutTypedVals,RelevantVariables,DoNotEnumVars,N1,TWF,OuterWF) |
331 | | ). |
332 | | |
333 | | |
334 | | l_ground_wait_flags(WF) :- |
335 | ? | l_ground_wait_flags(WF,WFE,Result), |
336 | | (Result=false |
337 | | -> (!,fail) % Cut: to prevent backtracking to find other solutions for earlier components ! |
338 | | % Assumption: each component is independent and finding other solution for earlier component would again lead to failure in last component checked by l_ground_wait_flags/2 |
339 | | ; grd_ef(WFE)). |
340 | | |
341 | | l_ground_wait_flags([],_,true). |
342 | | l_ground_wait_flags([wf_comp(_,ComponentNr)|TWF],WFE,SatResult) :- |
343 | | unsat_component(ComponentNr,_),!, % We now longer try and ground unknown components |
344 | | l_ground_wait_flags(TWF,WFE,SatResult). |
345 | | l_ground_wait_flags([wf_comp(WF,ComponentNr)|TWF],WFE,SatResult) :- |
346 | | (debug_mode(off) -> true ; print(grounding_component(ComponentNr)),nl, start_ms_timer(Timer)), |
347 | | % print_bt_message(grounding_component(ComponentNr)), portray_waitflags(WF),nl, %% |
348 | | % maybe we should set up new error scope ? <--- TO DO |
349 | ? | if(ground_constraintprop_wait_flags(WF), |
350 | | ((pending_abort_error(WF) -> assert_unsat_component_abort_error(ComponentNr) ; true), |
351 | | (debug_mode(on) -> (pending_abort_error(WF) -> Msg='PENDING WD/ABORT ERROR'; Msg='true'), |
352 | | stop_ms_timer_with_msg(Timer,component(ComponentNr,Msg)) |
353 | | ; true), |
354 | | get_enumeration_finished_wait_flag(WF,WFE), % bind enumeration finished to the global outer WFE flag |
355 | ? | l_ground_wait_flags(TWF,WFE,SatResult) % continue with other components |
356 | | ), |
357 | | ((debug_mode(on) -> print(unsatisfiable_component(ComponentNr)),nl, |
358 | | stop_ms_timer_with_msg(Timer,component(ComponentNr,'false')) ; true), |
359 | | store_unsat_component_nr(ComponentNr), |
360 | | SatResult=false)). |
361 | | |
362 | | project_typed_vals([],_,[],[]). |
363 | | project_typed_vals([typedval(Val,Type,VarID,EnumWarningInfos)|Rest],ComponentVars,In,Out) :- |
364 | ? | (member(VarID,ComponentVars) |
365 | | -> In = [typedval(Val,Type,VarID,EnumWarningInfos)|In2], Out2=Out |
366 | | ; Out = [typedval(Val,Type,VarID,EnumWarningInfos)|Out2], In2=In |
367 | | ), project_typed_vals(Rest,ComponentVars,In2,Out2). |
368 | | project_state([],_,[]). |
369 | | project_state([bind(VarID,Value)|Rest],ComponentVars,Out) :- |
370 | ? | (member(VarID,ComponentVars) |
371 | | -> Out=[bind(VarID,Value)|Out2] |
372 | | ; Out=Out2 |
373 | | ), project_state(Rest,ComponentVars,Out2). |
374 | | |
375 | | |
376 | | :- use_module(specfile,[get_specification_description/2]). |
377 | | :- use_module(state_packing,[pack_value/2,unpack_value/2]). |
378 | | :- volatile det_solution_for_constant_packed/2. |
379 | | :- dynamic det_solution_for_constant_packed/2. |
380 | | assert_det_solution_for_constant(Var,Val) :- |
381 | ? | (value_too_large(Val) -> PVal = '$TOO_LARGE' ,debug_println(4,not_storing(Var)) |
382 | | ; pack_value(Val,PVal) |
383 | | %,translate:translate_bvalue_with_limit(Val,200,VS),format(' STORING ---> ~w = ~w~n',[Var,VS]) |
384 | | ), |
385 | | assertz(det_solution_for_constant_packed(Var,PVal)). |
386 | | |
387 | | det_solution_for_constant(Var,Val) :- |
388 | | if(det_solution_for_constant_packed(Var,PVal), |
389 | | unpack_det_sol(Var,PVal,Val), |
390 | | (get_specification_description(properties,PROPS), |
391 | | ajoin([PROPS, ' are *not* true and no value was found (deterministically) for the following CONSTANT: '], Msg), |
392 | | add_message(det_value_not_found_for_constant,Msg,Var), |
393 | | fail) |
394 | | ). |
395 | | unpack_det_sol(Var,PVal,Val) :- |
396 | | (PVal = '$TOO_LARGE' |
397 | | -> ajoin(['The following CONSTANT was not stored due to its size (set preference ALLOW_INCOMPLETE_SETUP_CONSTANTS to TRUE to store all constants found): '], Msg), |
398 | | add_message(det_value_not_stored_for_constant,Msg,Var), |
399 | | fail |
400 | | ; unpack_value(PVal,Val) |
401 | | ). |
402 | | |
403 | | :- use_module(probsrc(translate), [translate_bvalue_kind/2]). |
404 | | get_det_solution_for_constant_value_kind(Var,ValKind) :- |
405 | | det_solution_for_constant_packed(Var,PVal), |
406 | | (PVal = '$TOO_LARGE' -> ValKind='LARGE-VALUE' |
407 | | ; unpack_value(PVal,Val), |
408 | | (translate_bvalue_kind(Val,K) -> ValKind=K ; ValKind='UNKNOWN-VALUE') |
409 | | ). |
410 | | |
411 | | det_solution_for_constant_was_stored :- (det_solution_for_constant_was_stored(_) -> true). |
412 | ? | det_solution_for_constant_was_stored(Var) :- det_solution_for_constant_packed(Var,_). |
413 | | |
414 | | :- use_module(probsrc(bsyntaxtree),[map_over_bexpr/2]). |
415 | | :- use_module(avl_tools,[avl_height_less_than/2]). |
416 | | value_too_large(avl_set(A)) :- |
417 | | preference(allow_incomplete_partial_setup_constants,false), %if true we always store the value |
418 | | (preference(provide_trace_information,false) -> MaxH=7 ; MaxH=9), |
419 | | \+ avl_height_less_than(A,MaxH). |
420 | | % TO DO: it could be a small set but include large subsets ! |
421 | | value_too_large((A,B)) :- (value_too_large(A) -> true ; value_too_large(B)). |
422 | ? | value_too_large(rec(F)) :- member(field(_,FVal),F), value_too_large(FVal). |
423 | | value_too_large(closure(P,T,B)) :- |
424 | | \+ custom_explicit_sets:is_interval_closure(P,T,B,_,_), |
425 | | preference(allow_incomplete_partial_setup_constants,false), |
426 | ? | uses_large_value(B). |
427 | | % we can have large symbolic closures; cf PlanProGross.mch; see also test 1637 |
428 | | |
429 | | % assumes values are all ground: |
430 | ? | uses_large_value_aux(value(V)) :- value_too_large(V). |
431 | | |
432 | | uses_large_value(BExpr) :- |
433 | ? | map_over_bexpr(uses_large_value_aux,BExpr). |
434 | | |
435 | | |
436 | | |
437 | | :- use_module(tools_printing,[print_value_summary/1]). |
438 | | reset_observe_state :- retractall(det_solution_for_constant_packed(_,_)). |
439 | | observe_state(Env,WF,ComponentNr) :- |
440 | | get_wait_flag0(WF,WF0), get_wait_flag1(observe_state,WF,WF1), |
441 | | (preferences:get_preference(provide_trace_information,true) |
442 | | -> when((ground(Env);nonvar(WF0)),print_success(WF0,ComponentNr,Env,WF)) |
443 | | %% ,when(nonvar(WF1),(nl, print('Starting Phase 1'),nl, portray_waitflags(WF))) |
444 | | ; true), |
445 | | observe_state2(Env,WF0,WF1). |
446 | | %,when(nonvar(WF0),(print(wf0),nl,portray_waitflags(WF))), when(nonvar(WF1),(print(wf1),nl)). |
447 | | |
448 | | :- use_module(debug,[debug_format/3]). |
449 | | print_success(WF0,ComponentNr,_,_WF) :- nonvar(WF0),!, |
450 | | debug_format(19,'~n-->> WF0 for component ~w grounded.~n',[ComponentNr]). |
451 | | print_success(_,ComponentNr,Env,_WF) :- |
452 | | (Env=[] |
453 | | -> |
454 | | format('~n-->> SUCCESS; processed component ~w (no identifiers)~n',[ComponentNr]) |
455 | | ; length(Env,NrVal), |
456 | | format('~n-->> SUCCESS; all ~w identifier(s) valued in component ~w~n',[NrVal,ComponentNr]) |
457 | | ), |
458 | | (debug_mode(on) |
459 | | -> findall(V,member(typedval(_,_,V,_),Env),Vars), |
460 | | statistics(walltime,[W,_]), format('-->> (walltime: ~w ms) : identifiers = ~w~n',[W,Vars]) |
461 | | ; true), |
462 | | %%,check_for_multiple_solutions(WF,ComponentNr) %% comment in to check for multiple solutions |
463 | | nl. |
464 | | |
465 | | observe_state2([],_,_). |
466 | | observe_state2([Binding|T],WF0,WF1) :- get_binding_value(Binding,VarID,Val), |
467 | | observe_variable(VarID,Val,WF0,WF1),observe_state2(T,WF0,WF1). |
468 | | |
469 | | get_binding_value(bind(Id,Value),Id,Value). |
470 | | get_binding_value(typedval(Value,_Type,Id,_Trigger),Id,Value). |
471 | | |
472 | | % print a warning message if we find multiple solutions *after* all constants have been valued |
473 | | %:- public check_for_multiple_solutions/2. |
474 | | %check_for_multiple_solutions(WF,ComponentNr) :- get_enumeration_finished_wait_flag(WF,EWF), |
475 | | % retractall(solution_found(ComponentNr,_)), |
476 | | % add_solution(EWF,ComponentNr). |
477 | | %:- volatile solution_found/2. |
478 | | %:- dynamic solution_found/2. |
479 | | %:- block add_solution(-,?). |
480 | | %add_solution(_,ComponentNr) :- retract(solution_found(ComponentNr,Nr)),!, |
481 | | % print('*** Multiple solutions found *after valuation* for component '), print(ComponentNr),nl, |
482 | | % N1 is Nr+1, |
483 | | % print('*** Solution: '),print(N1),nl, |
484 | | % assertz(solution_found(ComponentNr,N1)). |
485 | | %add_solution(_,ComponentNr) :- |
486 | | % assertz(solution_found(ComponentNr,1)). |
487 | | |
488 | | observe_variable(Var,Val,WF0,_WF1) :- preferences:get_preference(provide_trace_information,false),!, |
489 | | observe_variable_no_trace(Var,Val,WF0). |
490 | | observe_variable(Var,Val,WF0,WF1) :- |
491 | | ground_value_check(Val,GrVal), |
492 | | observe_variable0_block(Var,Val,GrVal,WF0,WF1). |
493 | | :- block observe_variable0_block(?,?,-,-,?). |
494 | | observe_variable0_block(Var,Val,GrVal,WF0,WF1) :- |
495 | | (var(GrVal) -> write(' :?: '), write(Var), write(' '), print_value_summary(Val), |
496 | | observe_variable1(Var,Val,WF1) |
497 | | ; (var(WF0) -> write('-->> ') |
498 | | ; write('--0-->> ')), % WF0/GrVal grounded at same time probably does not happen (often) |
499 | | write(Var),nl, write(' '),print_value_summary(Val),print_walltime,nl, |
500 | | assert_det_solution_for_constant(Var,Val) |
501 | | ). |
502 | | :- use_module(kernel_tools,[ground_value/1,ground_value_check/2]). |
503 | | :- block observe_variable_no_trace(?,-,-). |
504 | | observe_variable_no_trace(Var,Val,WF0) :- nonvar(WF0),!, % important e.g. for test 1368 |
505 | | (ground_value(Val) -> assert_det_solution_for_constant(Var,Val) ; true). |
506 | | observe_variable_no_trace(Var,Val,WF0) :- ground_value_check(Val,GV), |
507 | | %tools_printing:print_term_summary(gr_val_check(Var,Val,GV)),nl, |
508 | | observe_variable_no_trace4(Var,GV,Val,WF0). |
509 | | :- block observe_variable_no_trace4(?,-,?,-). |
510 | | observe_variable_no_trace4(_,GV,_,_) :- var(GV),!. |
511 | | observe_variable_no_trace4(Var,_,Val,_) :- assert_det_solution_for_constant(Var,Val). % this could be expensive !? |
512 | | |
513 | | print_walltime :- statistics(walltime,[Tot,Delta]), |
514 | | (debug_mode(on) -> print(' '),tools:print_memory_used_wo_gc ; true), |
515 | | format(' ~w ms (Delta ~w ms) ',[Tot,Delta]). |
516 | | |
517 | | observe_variable1(Var,Val,WF1) :- ground_value_check(Val,GV), |
518 | | observe_variable1_block(Var,Val,WF1,GV). |
519 | | :- block observe_variable1_block(?,?,-,-). |
520 | | observe_variable1_block(Var,Val,_WF1,GV):- |
521 | | (nonvar(GV) |
522 | | -> write('--1-->> '), write(Var),nl, write(' '),print_value_summary(Val), |
523 | | assert_det_solution_for_constant(Var,Val) |
524 | | ; write(' :?1?: '), write(Var), write(' '), print_value_summary(Val) |
525 | | %% , when(ground(Val), (print('--ground-->> '),print(Var),print(' '), print_value_summary(Val))) % comment in to see grounding |
526 | | %% , external_functions:observe_state([bind(Var,Val)]) % comment in to observe values |
527 | | %% , translate:print_bstate([bind(Var,Val)]),nl % comment in to print full value with CLP(FD) info |
528 | | ). |
529 | | |
530 | | |
531 | | % -------------------- |
532 | | |
533 | | construct_optimized_exists(Parameters,Body,Pred) :- |
534 | | construct_optimized_exists(Parameters,Body,Pred,true,_). |
535 | | construct_optimized_exists(Parameters,Body,Pred,Simplify) :- |
536 | ? | construct_optimized_exists(Parameters,Body,Pred,Simplify,_). |
537 | | |
538 | | % we could also partition inside b_interpreter ? |
539 | | construct_optimized_exists(Parameters,Body,Pred,Simplify,NrComponents) :- |
540 | | % we can actually do more: first project out all conjuncts which do not have any variables in common with Parameters |
541 | | % #x.(P(x) & Q) <=> #x.(P(x)) & Q |
542 | | % we can also use a stronger version of predicate components #(x,y).( P(x,v) & Q(v,y)) <=> #(x).P(x,v) & #(y).Q(v,y) |
543 | | get_texpr_ids(Parameters,PIDs), |
544 | | %print(constructing_optimized_exists(PIDs)),nl, |
545 | | LocalVars = PIDs, |
546 | | predicate_components_with_restriction(Body,LocalVars,PIDs,Components), % we focus partition decision on PIDs, not on all identifiers |
547 | | %length(Components,Len), print(nr_of_components_for_exists(Len,Components)),nl, |
548 | ? | conjunct_components(Components,Parameters,Pred,Simplify,NrComponents). |
549 | | % print('* EXISTS: '),translate:print_bexpr(Pred),nl. |
550 | | |
551 | | :- use_module(bsyntaxtree). |
552 | | conjunct_components(C,Parameters,Res,Simplify,NrComponents) :- |
553 | ? | conjunct_components2(C,Parameters,List,Simplify,0,NrComponents), |
554 | | conjunct_predicates_with_pos_info(List,Res). |
555 | | |
556 | | conjunct_components2([],_Parameters,[],_,Nr,Nr). % :- (Parameters=[] -> true ; print(unused_existential_var(Parameters)),nl). |
557 | | conjunct_components2([component(Pred,ComponentVars)|T],Parameters,Res,Simplify,InNr,OutNr) :- |
558 | | augment_ids(ComponentVars,CV), % translate atomic id names into b(identifier(... |
559 | | tools:list_intersection(CV,Parameters,ExistParas), % TO DO: use ordsets |
560 | | (Simplify=true |
561 | | -> create_and_simplify_exists(ExistParas,Pred,EP) % no use in recursively calling construct_optimized_exists |
562 | | ; Simplify= no_cleanup_and_simplify |
563 | | -> create_exists(ExistParas,Pred,EP0), % avoid loops when called from ast_cleanup |
564 | | get_texpr_info(EP0,AllInfos0), |
565 | | (nonmember(nodeid(_),AllInfos0),ExistParas=[Par1|_] |
566 | | -> get_texpr_info(Par1,AllInfos1),extract_pos_infos(AllInfos1,PIs) % try and get position from ids |
567 | | ; PIs=[]), % we already have a position info |
568 | | add_texpr_infos(EP0,[partitioned_exists|PIs],EP) % add ,removed_typing ?? |
569 | ? | ; create_unsimplified_exists(ExistParas,Pred,EP) |
570 | | ), |
571 | | (EP=b(truth,pred,[]) % or should we allow _ for Info field ?? |
572 | | -> Res=TRes |
573 | | ; Res=[EP|TRes]), |
574 | | In1 is InNr+1, |
575 | ? | conjunct_components2(T,Parameters,TRes,Simplify,In1,OutNr). |
576 | | |
577 | | |
578 | | % translate a list of atomic ides into ids with the b/3 wrapper |
579 | | augment_ids([],[]). |
580 | | augment_ids([ID|T],[b(identifier(ID),_,_)|TT]) :- augment_ids(T,TT). |
581 | | |
582 | | % extract and inline simple equalities to remove quantified variables; useful for exists and forall |
583 | | % TO DO: move to b_quantifier_optimizations.pl or similar |
584 | | extract_equalities_in_quantifier(Parameters,Pred, ReducedParas,OutPred) :- |
585 | | extract_equalities2(Pred,Parameters,ReducedParas,NewPred,[],Subst), |
586 | | %sort(Subst,SortedSubst), |
587 | | %print(extracted(Subst)),nl, |
588 | | apply_subst(Subst,NewPred,OutPred). |
589 | | % apply equality inside expressions: note: this may duplicate expressions ! |
590 | | % However, it also works if the equalities contain parameters |
591 | | |
592 | | :- use_module(bsyntaxtree,[is_equality/3]). |
593 | | |
594 | | extract_equalities2(b(conjunct(A,B),pred,Info),Ids,RemIds,Res) --> !, |
595 | | {Res=b(conjunct(PA,PB),pred,Info)}, |
596 | | extract_equalities2(A, Ids,RemIds1,PA), |
597 | | extract_equalities2(B,RemIds1,RemIds,PB). |
598 | | extract_equalities2(TEqual,Ids,RemIds,NewPred,InSubst,OutSubst) :- |
599 | | is_equality(TEqual,ID,E), |
600 | | % TODO: we could try and split equalities if possible; usually this is done by ast_cleanup though? |
601 | | %(bsyntaxtree:split_equality(TEqual,A,B) -> nl,print('COULD BE SPLIT: '),translate:print_bexpr(TEqual),nl ; true), |
602 | | same_id_is_member(ID,Ids,SID,RemIds), |
603 | | %\+ occurs_in_expr(SID,E), % not cyclic; we check it again after applyng InSubst to E below |
604 | | \+ in_subst_dom(SID,InSubst), % ensure we don't already have an equation for the same ID |
605 | | compose_with_subst(InSubst,SID,E,OutSubst), % we can compose with existing substitution without occurs-check problem |
606 | | always_well_defined_or_disprover_mode(E), % otherwise we may move evaluation of E forward ! |
607 | | !, |
608 | | NewPred=b(truth,pred,[]). |
609 | | extract_equalities2(X,Ids,Ids,X) --> []. |
610 | | |
611 | | |
612 | | |
613 | | % compose a new binding Var/Expr with existing substitution Subst; where Subst was not yet applied to Expr |
614 | | compose_with_subst(Subst,Var,Expr,[Var/NewExpr|ModSubst]) :- |
615 | | apply_subst(Subst,Expr,NewExpr), |
616 | | \+ occurs_in_expr(Var,NewExpr), % otherwise cyclic |
617 | | apply_binding_to_substitution(Subst,Var,NewExpr,ModSubst). |
618 | | apply_binding_to_substitution([],_,_,[]). |
619 | | apply_binding_to_substitution([TV/TE|T],Var,Expr,[TV/TE2|NT]) :- |
620 | | \+ occurs_in_expr(TV,Expr), % otherwise we get a cyclic substitution --> fail |
621 | | replace_id_by_expr(TE,Var,Expr,TE2), |
622 | | apply_binding_to_substitution(T,Var,Expr,NT). |
623 | | |
624 | | apply_subst([],X,X). |
625 | | apply_subst([Var/Expr|T],E,NewE) :- |
626 | | (replace_id_by_expr(E,Var,Expr,E2) % replace identifier Var by expression Expr |
627 | | -> %apply_to_tail(T,Var,Expr,NT), % apply binding to tail of substitution |
628 | | apply_subst(T,E2,NewE) |
629 | | ; add_internal_error('Call failed: ',apply_subst([Var/Expr|T],E,NewE)),NewE=E). |
630 | | |
631 | | |
632 | | in_subst_dom(X,[I/_|T]) :- (X=I -> true ; in_subst_dom(X,T)). |
633 | | |
634 | | same_id_is_member(X,[H|T],SID,Rest) :- |
635 | | (same_id(X,H,SID) -> Rest=T ; Rest=[H|TR],same_id_is_member(X,T,SID,TR)). |
636 | | |
637 | | % TO DO: investigate what is the exact difference with create_exists_opt; partition into components is only done by construct_optimized_exists/3 |
638 | | create_and_simplify_exists(Paras,Pred,ResultingPred) :- |
639 | | extract_equalities_in_quantifier(Paras,Pred,NewParas,NewPred), |
640 | | create_and_simplify_exists2(NewParas,NewPred,ResultingPred). |
641 | | |
642 | | %:- use_module(b_interpreter_check,[arithmetic_op/4]). % TODO REFACTOR and move this and code below to separate module |
643 | | create_and_simplify_exists2([],Pred,R) :- !,R=Pred. |
644 | | % simplify #ID.(ID:E1 & ID:E2) <=> E1 /\ E2 /= {} , etc... now done in ast_cleanup |
645 | | % simplify #ID.(ID/:E) <=> E /= FullType also done in ast_cleanup |
646 | | % simplify #SID.(SID > Expr) <=> truth now done in ast_cleanup |
647 | | create_and_simplify_exists2(Parameters,Body,Res) :- create_unsimplified_exists(Parameters,Body,Res). |
648 | | % TO DO: treat nested exists |
649 | | |
650 | | |
651 | | |
652 | | :- use_module(b_ast_cleanup,[clean_up_pred/3]). |
653 | | :- use_module(bsyntaxtree,[add_texpr_info_if_new/3]). |
654 | | create_unsimplified_exists(Parameters,Body,Res) :- |
655 | | maplist(mark_generated_exists,Parameters,Para2), % mark that this is an artificially constructed; we now mark the exists |
656 | | create_exists(Para2,Body,Res1), |
657 | | add_texpr_info_if_new(Res1,removed_typing,Res2), % unconditionally add this info, avoid generating warnings in exists_body_warning, see test 625 |
658 | | % and avoid useless_let_message |
659 | | % previously removed_typing was only added for disjuncts or implications |
660 | ? | clean_up_pred(Res2,[],Res). % also computes used_ids, |
661 | | % and performs some existential quantifier optimisations like replace_exists_by_not_empty |
662 | | |
663 | | mark_generated_exists(TID1,TID2) :- add_texpr_info_if_new(TID1,generated_exists_parameter,TID2). |