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).