1 % (c) 2009-2019 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(b_interpreter_components,[
6 reset_component_info/1,
7 b_trace_test_components/3, b_trace_test_components/4,
8 unsat_component/2, unsat_component_enumeration_warning/1,
9 unsat_or_unknown_component_exists/0,
10 unsat_component_abort_error/1,
11 unsat_conjunct_inside_component/4,
12 det_solution_for_constant/2,
13 b_tracetest_boolean_expression/5,
14 observe_state/3,
15
16 construct_optimized_exists/3, construct_optimized_exists/4
17 ]).
18
19 :- use_module(b_interpreter,[b_test_boolean_expression/6]).
20 :- use_module(preferences).
21 :- use_module(kernel_waitflags).
22 :- use_module(tools).
23 :- use_module(translate).
24 :- use_module(error_manager).
25 :- use_module(library(lists)).
26 :- use_module(b_enumerate, [b_trace_tighter_enumerate_values/2]).
27
28 :- load_files(library(system), [when(compile_time), imports([environ/2])]).
29 :- if(\+ environ(disable_chr, true)).
30 :- use_module(chrsrc(chr_integer_inequality),[attach_chr_integer_inequality_to_wf_store/1]).
31 :- else.
32 attach_chr_integer_inequality_to_wf_store(_).
33 :- endif.
34
35 :- use_module(module_information,[module_info/2]).
36 :- module_info(group,interpreter).
37 :- module_info(description,'This module can be used to split a predicate into components and solving each component individually (with tracing feedback).').
38
39 /* -------------- */
40 /* dynamic bits */
41 /* -------------- */
42
43 :- dynamic skipped_unsat_conjunct/1, unsat_conjunct_inside_component/4.
44 reset_unsat_conjunct_inside_component_info :-
45 retractall(skipped_unsat_conjunct(_)),
46 retractall(unsat_conjunct_inside_component(_,_,_,_)).
47 reset_tracetest_for_component :- retractall(skipped_unsat_conjunct(_)). % to be called before trace checking a component
48
49 % individual conjuncts which are directly unsatisfiable
50 assert_skipped_failed_conjunct(ComponentNr,BE) :- assert_skipped_unsat_conjunct,
51 assert(unsat_conjunct_inside_component(ComponentNr,BE,false,'')),
52 (preferences:get_preference(provide_trace_information,false) -> true
53 ; print(' *** INCONSISTENCY DETECTED, SKIPPING: '),
54 (debug_mode(on) -> print_bexpr_with_limit(BE,500) ; print_bexpr_with_limit(BE,150)),
55 nl,
56 print(' *** COMPONENT NUMBER: '), print(ComponentNr),nl).
57 assert_skipped_unknown_conjunct(ComponentNr,BE,Reason) :- assert_skipped_unknown_conjunct,
58 assert(unsat_conjunct_inside_component(ComponentNr,BE,unknown,Reason)),
59 (preferences:get_preference(provide_trace_information,false) -> true
60 ; get_reason_msg(Reason,MSG),format(' *** Skipping predicate due to ~w:',[MSG]),
61 print_bexpr_with_limit(BE,150),nl,
62 format('~n *** Predicate is unknown~n *** COMPONENT NUMBER: ~w~n',[ComponentNr])
63 ).
64 assert_skipped_unsat_conjunct :-
65 (skipped_unsat_conjunct(false) -> true
66 ; retractall(skipped_unsat_conjunct(_)), assert(skipped_unsat_conjunct(false))).
67 assert_skipped_unknown_conjunct :-
68 (skipped_unsat_conjunct(_) -> true ; assert(skipped_unsat_conjunct(unknown))).
69
70 get_reason_msg(enumeration_warning,R) :- !, R='ENUMERATION WARNING'.
71 get_reason_msg('prob-ignore',R) :- !, R='prob-ignore pragma'.
72 get_reason_msg(M,M).
73
74
75 :- dynamic unsat_component/2, unsat_component_enumeration_warning/1, unsat_component_abort_error/1.
76 reset_unsat_component_info :-
77 retractall(unsat_component(_,_)),
78 retractall(unsat_component_enumeration_warning(_)),
79 retractall(unsat_component_abort_error(_)).
80
81 :- use_module(eventhandling,[register_event_listener/3]).
82 :- register_event_listener(clear_specification,reset_unsat_component_info,
83 'Reset b_interpreter_components unsat component info.').
84
85 store_unsat_component_nr(X,FalseOrUnknown) :- debug_println(19,store_unsat_component_nr(X,FalseOrUnknown)),
86 (unsat_component(X,false) -> true
87 ; retractall(unsat_component(X,_)),assert(unsat_component(X,FalseOrUnknown))
88 ),
89 (enumeration_warning_occured_in_error_scope,
90 \+ unsat_component_enumeration_warning(X)
91 -> assert(unsat_component_enumeration_warning(X)),
92 debug_println(19,enumeration_warning_in_component(X))
93 ; true),
94 (abort_error_occured_in_error_scope,
95 \+ unsat_component_abort_error(X)
96 -> assert(unsat_component_abort_error(X)), print(abort(X)),nl,
97 debug_println(19,unsat_component_abort_error(X))
98 ; true).
99 ?unsat_component_exists :- unsat_component(_,false),!.
100 unsat_or_unknown_component_exists :- unsat_component(_,_),!.
101
102 :- public nr_of_components/1.
103 :- dynamic nr_of_components/1.
104 :- dynamic allow_skipping_over_components/0.
105 reset_component_info(AllowSkip) :- retractall(allow_skipping_over_components),
106 (AllowSkip==true -> assert(allow_skipping_over_components) ; true),
107 reset_unsat_component_info,
108 reset_unsat_conjunct_inside_component_info.
109
110 % 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 !!
111 % TO DO: keep track of enumeration warnings per component
112
113 /* -------------- */
114
115
116 :- use_module(library(avl)).
117 % You can use the term one as ComponentNr if you don't know the number or it is not relevant
118 b_tracetest_boolean_expression(BE,LS,S,WF,ComponentNr) :-
119 empty_avl(Ai),
120 b_tracetest_boolean_expression1(BE,LS,S,WF,ComponentNr,Ai,_Ao).
121
122
123 b_tracetest_boolean_expression1(b(Expr,_,Infos),LS,S,WF,ComponentNr,Ai,Ao) :- !,
124 b_tracetest_boolean_expression2(Expr,Infos,LS,S,WF,ComponentNr,Ai,Ao).
125
126 :- use_module(bsyntaxtree,[info_has_ignore_pragma/1]).
127
128 b_tracetest_boolean_expression2(BE,Infos,_LS,_S,_WF,ComponentNr,Ai,Ao) :-
129 get_preference(use_ignore_pragmas,true),
130 info_has_ignore_pragma(Infos),!,
131 format('~n ==IGNORING==> (~w) ',[ComponentNr]),print_bexpr_with_limit(BE,250),nl,
132 assert_skipped_unknown_conjunct(ComponentNr,b(BE,pred,Infos),'prob-ignore'),
133 Ai=Ao.
134 b_tracetest_boolean_expression2(conjunct(LHS,RHS),_Info,LocalState,State,WF,ComponentNr,Ai,Ao) :-
135 % \+(Info = [try_smt|_]),
136 !,
137 %% print('check lhs of conjunct: '), translate:print_bexpr(LHS),nl,
138 b_tracetest_boolean_expression1(LHS,LocalState,State,WF,ComponentNr,Ai,Aii),
139 %% print('check rhs of conjunct: '), translate:print_bexpr(RHS),nl,
140 b_tracetest_boolean_expression1(RHS,LocalState,State,WF,ComponentNr,Aii,Ao).
141 b_tracetest_boolean_expression2(BE,Infos,LS,S,WF,ComponentNr,Ai,Ao) :-
142 (get_preference(provide_trace_information,true)
143 -> format('~n ====> (~w) ',[ComponentNr]),print_bexpr_with_limit(BE,250),nl
144 ; true),
145 if(safe_b_test_boolean_expression(ComponentNr,b(BE,pred,Infos),LS,S,WF,Ai,Ao),true, %% b_det_test_boolean_expression2
146 (assert_skipped_failed_conjunct(ComponentNr,b(BE,pred,Infos)),
147 Ai=Ao)).
148
149 safe_b_test_boolean_expression(ComponentNr,BE,LS,S,WF,Ai,Ao) :-
150 catch_enumeration_warning_exceptions(
151 b_test_boolean_expression(BE,LS,S,WF,Ai,Ao), % this should not backtrack (without WF0 set); otherwise we may get problems with error blocks nesting
152 (assert_skipped_unknown_conjunct(ComponentNr,BE,enumeration_warning),Ai=Ao), true). % true: add exceptions as event errors
153
154 :- use_module(bsyntaxtree,[predicate_components_in_scope/3,
155 predicate_identifiers/2,predicate_identifiers_in_scope/3]).
156
157 get_typedval_id(typedval(_Val,_Type,ID,_),ID).
158 b_trace_test_components(Properties,ConstantsState,TypedVals) :-
159 %format('b_trace_test_components(~w,~w,~w)',[Properties,ConstantsState,TypedVals]),
160 ? b_trace_test_components(Properties,ConstantsState,TypedVals,all).
161 b_trace_test_components(Properties,ConstantsState,TypedVals,RelevantVariables) :-
162 ? b_trace_test_components(Properties,ConstantsState,TypedVals,RelevantVariables,[]).
163 % in constract to /3 and /4 b_trace_test_components/5 does not fail if the predicate is unsatisfiable.
164 % instead it returns the unsatisfiable component as the last argument
165 % the other predicates still fail because they expect to find an empty list there
166 b_trace_test_components(Properties,ConstantsState,TypedVals,RelevantVariables,UnsatConjuncts) :-
167 % we now treat all TypedVals as local variables: ensure that we count them in predicate_components/predicate_identifiers (in case they clash with global identifiers)!
168 ? maplist(get_typedval_id,TypedVals,LocalVars),
169 ? (preferences:get_preference(partition_predicates,true)
170 -> predicate_components_in_scope(Properties,LocalVars,Components),
171 length(Components,Len), print_trace_message(nr_of_components(Len))
172 ; predicate_identifiers_in_scope(Properties,LocalVars,PropVars),
173 Components = [component(Properties,PropVars)], Len=1
174 ),
175 ? retractall(nr_of_components(_)),assert(nr_of_components(Len)),
176 ? reset_observe_state,
177 % print(components(Components)),nl,
178 ? b_trace_test_components2(Components,ConstantsState,TypedVals,RelevantVariables,1,WFList),
179 ? get_unsat_component(WFList,Components,UnsatConjuncts).
180
181
182 get_unsat_component(_,Components,UnsatConjuncts) :-
183 unsat_component_exists, !,
184 debug_println(9,unsatisfiable_predicate),
185 ? unsat_component(UnsatComponentNr,false),!,
186 nth0(UnsatComponentNr,Components,component(UnsatConjuncts,_)).
187 get_unsat_component(WFList,Components,UnsatConjuncts) :-
188 ? get_unsat_component_aux(WFList,Components,UnsatConjuncts).
189
190 get_unsat_component_aux(WFList,Components,UnsatConjuncts) :-
191 ? print_trace_message(grounding_wait_flags), % Note: for unknown components we continue grounding
192 ? l_ground_wait_flags(WFList),
193 (unsat_component(UnsatComponentNr,_)
194 -> print(we_have_unsat(UnsatComponentNr)),nl,
195 nth0(UnsatComponentNr,Components,component(UnsatConjuncts,_))
196 ; UnsatConjuncts=[]).
197 get_unsat_component_aux(_,Components,UnsatConjuncts) :-
198 unsat_component(UnsatComponentNr,_),!,
199 nth0(UnsatComponentNr,Components,component(UnsatConjuncts,_)).
200
201
202 :- use_module(b_ast_cleanup,[predicate_level_optimizations/2]).
203 :- use_module(debug,[printsilent_message/1]).
204 print_trace_message(Msg) :-
205 (preference(provide_trace_information,false) -> true
206 ; printsilent_message(Msg)).
207 print_trace_message_with_typed_vals(Msg,TypedVals) :-
208 (preference(provide_trace_information,false) -> true
209 ; printsilent_message(Msg),
210 maplist(get_typedval_id,TypedVals,IDs),
211 printsilent_message(IDs)
212 ).
213
214 b_trace_test_components2([],_ConstantsState,TypedVals,_,Nr,ResWF) :-
215 (TypedVals=[] -> print_trace_message(finished_processing_component_predicates),
216 ResWF=[]
217 ; print_trace_message_with_typed_vals('Enumerating Identifiers without constraints: ',TypedVals),
218 init_wait_flags(WF),
219 b_trace_tighter_enumerate_values(TypedVals,WF), ResWF=[wf_comp(WF,Nr)]).
220 b_trace_test_components2([component(Pred,ComponentVars)|T],ConstantsState,TypedVals,RelevantVariables,
221 Nr,ResWF) :-
222 N1 is Nr+1,
223 ((RelevantVariables\=all, ComponentVars\= all,
224 lists_are_disjoint(RelevantVariables,ComponentVars))
225 -> print_trace_message(skipping_irrelevant_component(Nr,ComponentVars)),
226 b_trace_test_components2(T,ConstantsState,TypedVals,RelevantVariables,N1,ResWF)
227 ; debug_println(9,checking_component_properties(Nr,ComponentVars)), %%
228 %% print(component(Nr)),nl, translate:nested_print_bexpr_as_classicalb(Pred),debug:nl_time,
229 ResWF = [wf_comp(WF,Nr)|TWF],
230 init_wait_flags(WF),
231 attach_chr_integer_inequality_to_wf_store(WF),
232 (ComponentVars=all -> InTypedVals=TypedVals, OutTypedVals=[], ProjectedState=ConstantsState
233 ; project_typed_vals(TypedVals,ComponentVars,InTypedVals,OutTypedVals),
234 project_state(ConstantsState,ComponentVars,ProjectedState) % relevant, e.g., for exists which may wait on state becoming ground
235 ),
236 observe_state(InTypedVals,WF,Nr), %%
237 reset_tracetest_for_component,
238 predicate_level_optimizations(Pred,OPred),
239 %%nl,nl,print(component(Nr,ComponentVars,ProjectedState)),nl,nl,translate:print_bexpr(Pred),nl,nl,
240 b_tracetest_boolean_expression(OPred,[],ProjectedState,WF,Nr),
241 %print(done_comp(Nr,WF,ComponentVars)),nl,
242 (skipped_unsat_conjunct(false)
243 -> store_unsat_component_nr(Nr,false),
244 allow_skipping_over_components % check if we allow skipping over unsat components to maybe find as many values for constants as possible ,...
245 % Note: in Disprover mode we can stop and fail straightaway
246 ; %print_bt_message(setup_enumerating_constants(Nr)), %%(TypedVals)), %%
247 (skipped_unsat_conjunct(_) -> store_unsat_component_nr(Nr,unknown) ; true), % probably due to prob-ignore
248 b_trace_tighter_enumerate_values(InTypedVals,WF),
249 (preferences:get_preference(disprover_mode,false) -> true
250 ; ground_wait_flag0(WF) % ensure that we give each component the chance to run WF0 inconsistency checks
251 %, print(finished_wf0_grounding_in_disprover_mode(Nr)),nl
252 )
253 ),
254 % we could ground waitflag 1 (deterministic computations) ?
255 b_trace_test_components2(T,ConstantsState,OutTypedVals,RelevantVariables,N1,TWF)
256 ).
257
258 :- use_module(debug,[debug_println/2, debug_mode/1]).
259 :- use_module(tools,[start_ms_timer/1, stop_ms_timer_with_msg/2]).
260
261 l_ground_wait_flags(WF) :-
262 ? l_ground_wait_flags(WF,Result),
263 (Result=false
264 -> (!,fail) % Cut: to prevent backtracking to find other solutions for earlier components !
265 % 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
266 ; true).
267
268 l_ground_wait_flags([],true).
269 l_ground_wait_flags([wf_comp(WF,ComponentNr)|TWF],SatResult) :- %trace,
270 ? (debug_mode(off) -> true ; print(grounding_component(ComponentNr)),nl, start_ms_timer(Timer)),
271 % print_bt_message(grounding_component(ComponentNr)), portray_waitflags(WF),nl, %%
272 % maybe we should set up new error scope ? <--- TO DO
273 ? if(ground_wait_flags(WF),
274 ((debug_mode(on) -> stop_ms_timer_with_msg(Timer,component(ComponentNr)) ; true),
275 l_ground_wait_flags(TWF,SatResult) % continue with other components
276 ),
277 ((debug_mode(on) -> print(unsatisfiable_component(ComponentNr)),nl,
278 stop_ms_timer_with_msg(Timer,component(ComponentNr)) ; true),
279 store_unsat_component_nr(ComponentNr,false),
280 SatResult=false)).
281
282 project_typed_vals([],_,[],[]).
283 project_typed_vals([typedval(Val,Type,VarID,EnumWarning)|Rest],ComponentVars,In,Out) :-
284 ? (member(VarID,ComponentVars)
285 -> In=[typedval(Val,Type,VarID,EnumWarning)|In2], Out2=Out
286 ; Out=[typedval(Val,Type,VarID,EnumWarning)|Out2], In2=In
287 ), project_typed_vals(Rest,ComponentVars,In2,Out2).
288 project_state([],_,[]).
289 project_state([bind(VarID,Value)|Rest],ComponentVars,Out) :-
290 ? (member(VarID,ComponentVars)
291 -> Out=[bind(VarID,Value)|Out2]
292 ; Out=Out2
293 ), project_state(Rest,ComponentVars,Out2).
294
295
296 :- use_module(state_packing,[pack_value/2,unpack_value/2]).
297 :- volatile det_solution_for_constant_packed/2.
298 :- dynamic det_solution_for_constant_packed/2.
299 assert_det_solution_for_constant(Var,Val) :- %print(sol(Var)),tools:print_memory_used_wo_gc,nl,nl,
300 (value_too_large(Val) -> PVal = '$TOO_LARGE' ,debug_println(4,not_storing(Var))
301 ; pack_value(Val,PVal)),
302 assert(det_solution_for_constant_packed(Var,PVal)).
303 %,tools:print_memory_used_wo_gc,nl.
304 ?det_solution_for_constant(Var,Val) :- det_solution_for_constant_packed(Var,PVal),
305 (PVal = '$TOO_LARGE'
306 -> add_message(no_det_value_stored_for_constant,'Values were found for some constants but not stored. Set ALLOW_INCOMPLETE_SETUP_CONSTANTS to TRUE to store constants found, e.g.: ',Var),
307 fail
308 ; unpack_value(PVal,Val)
309 ).
310
311 :- use_module(avl_tools,[avl_height_less_than/2]).
312 value_too_large(avl_set(A)) :-
313 preference(allow_incomplete_partial_setup_constants,false), %if true we always store the value
314 \+ avl_height_less_than(A,7).
315 % TO DO: it could be a small set but include large subsets !
316
317 :- use_module(tools_printing,[print_term_summary/1]).
318 reset_observe_state :- retractall(det_solution_for_constant_packed(_,_)).
319 observe_state(Env,WF,ComponentNr) :-
320 get_wait_flag0(WF,WF0), get_wait_flag1(observe_state,WF,WF1),
321 (preferences:get_preference(provide_trace_information,true)
322 -> when((ground(Env);nonvar(WF0)),print_success(WF0,ComponentNr,Env,WF))
323 %% ,when(nonvar(WF1),(nl, print('Starting Phase 1'),nl, portray_waitflags(WF)))
324 ; true),
325 observe_state2(Env,WF0,WF1).
326 %,when(nonvar(WF0),(print(wf0),nl,portray_waitflags(WF))), when(nonvar(WF1),(print(wf1),nl)).
327
328 :- use_module(debug,[debug_format/3]).
329 print_success(WF0,ComponentNr,_,_WF) :- nonvar(WF0),!,
330 debug_format(19,'~n-->> WF0 for component ~w grounded.~n',[ComponentNr]).
331 print_success(_,ComponentNr,Env,_WF) :-
332 format('~n-->> SUCCESS; all constants valued in component ~w~n',[ComponentNr]),
333 (debug_mode(on)
334 -> findall(V,member(typedval(_,_,V,_),Env),Vars),
335 statistics(walltime,[W,_]), format('-->> (walltime: ~w ms) : variables = ~w~n',[W,Vars])
336 ; true),
337 %%,check_for_multiple_solutions(WF,ComponentNr) %% comment in to check for multiple solutions
338 nl.
339
340 observe_state2([],_,_).
341 observe_state2([typedval(Val,_Type,VarID,_EnumWarning)|T],WF0,WF1) :-
342 observe_variable(VarID,Val,WF0,WF1),observe_state2(T,WF0,WF1).
343 %observe_state2([bind(Var,Val)|T],WF0) :- observe_variable(Var,Val,WF0), observe_state2(T,WF0).
344
345 % print a warning message if we find multiple solutions *after* all constants have been valued
346 %:- public check_for_multiple_solutions/2.
347 %check_for_multiple_solutions(WF,ComponentNr) :- get_enumeration_finished_wait_flag(WF,EWF),
348 % retractall(solution_found(ComponentNr,_)),
349 % add_solution(EWF,ComponentNr).
350 %:- volatile solution_found/2.
351 %:- dynamic solution_found/2.
352 %:- block add_solution(-,?).
353 %add_solution(_,ComponentNr) :- retract(solution_found(ComponentNr,Nr)),!,
354 % print('*** Multiple solutions found *after valuation* for component '), print(ComponentNr),nl,
355 % N1 is Nr+1,
356 % print('*** Solution: '),print(N1),nl,
357 % assert(solution_found(ComponentNr,N1)).
358 %add_solution(_,ComponentNr) :- % print(add_sol(ComponentNr)),nl,
359 % assert(solution_found(ComponentNr,1)).
360
361 observe_variable(Var,Val,WF0,_WF1) :- preferences:get_preference(provide_trace_information,false),!,
362 observe_variable_no_trace(Var,Val,WF0).
363 observe_variable(Var,Val,WF0,WF1) :- ground_value_check(Val,GrVal),
364 when((nonvar(GrVal);nonvar(WF0)),
365 (var(WF0) -> print('-->> '),print(Var), nl,print(' '),print_term_summary(value(Val)),print_walltime,nl,
366 assert_det_solution_for_constant(Var,Val)
367 ; print(' :?: '),print(Var),print(' '), print_term_summary(value(Val)),
368 observe_variable1(Var,Val,WF1)
369 )
370 ).
371 :- use_module(kernel_tools,[ground_value/1,ground_value_check/2]).
372 :- block observe_variable_no_trace(?,-,-).
373 observe_variable_no_trace(Var,Val,WF0) :- nonvar(WF0),!, % important e.g. for test 1368
374 (ground_value(Val) -> assert_det_solution_for_constant(Var,Val) ; true).
375 observe_variable_no_trace(Var,Val,WF0) :- ground_value_check(Val,GV),
376 %print_term_summary(gr_val_check(Var,Val,GV)),nl,
377 observe_variable_no_trace4(Var,GV,Val,WF0).
378 :- block observe_variable_no_trace4(?,-,?,-).
379 observe_variable_no_trace4(_,GV,_,_) :- var(GV),!.
380 observe_variable_no_trace4(Var,_,Val,_) :- assert_det_solution_for_constant(Var,Val). % this could be expensive !?
381
382 print_walltime :- statistics(walltime,[Tot,Delta]),
383 (debug_mode(on) -> print(' '),tools:print_memory_used_wo_gc ; true),
384 format(' ~w ms (Delta ~w ms) ',[Tot,Delta]).
385
386 observe_variable1(Var,Val,WF1) :- ground_value_check(Val,GV),
387 when((nonvar(GV);nonvar(WF1)),
388 (nonvar(GV) -> print('--1-->> '),print(Var), nl,print(' '),print_term_summary(value(Val)),
389 assert_det_solution_for_constant(Var,Val)
390 ; print(' :?1?: '),print(Var),print(' '), print_term_summary(value(Val))
391 %% , when(ground(Val), (print('--ground-->> '),print(Var),print(' '), print_term_summary(value(Val)))) % comment in to see grounding
392 %% , external_functions:observe_state([bind(Var,Val)]) % comment in to observe values
393 %% , translate:print_bstate([bind(Var,Val)]),nl % comment in to print full value with CLP(FD) info
394 ) ).
395
396 /*
397 comment back in for finer-grained observation and uncomment call above
398 :- block observe_instantiation(-,?,?).
399 observe_instantiation(X,Var,Val) :-
400 print(bind(X)),nl,translate:print_bstate([bind(Var,Val)]),nl, (debug_mode(on) -> trace ; true),
401 X =.. [_|Args],
402 observe2(Args,Var,Val).
403
404 observe2([],_,_).
405 observe2([H|T],Var,Val) :- (var(H) -> observe_instantiation(H,Var,Val)
406 ; H=.. [_|Args], observe2(Args,Var,Val)),
407 observe2(T,Var,Val).
408 */
409
410 % --------------------
411
412
413 construct_optimized_exists(Parameters,Body,Pred) :-
414 construct_optimized_exists(Parameters,Body,Pred,true).
415
416 % we could also partition inside b_interpreter ?
417 construct_optimized_exists(Parameters,Body,Pred,Simplify) :-
418 % we can actually do more: first project out all conjuncts which do not have any variables in common with Parameters
419 % #x.(P(x) & Q) <=> #x.(P(x)) & Q
420 % 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)
421 get_texpr_ids(Parameters,PIDs),
422 %print(constructing_optimized_exists(PIDs)),nl,
423 predicate_components_with_restriction(Body,[],PIDs,Components), % should we use LocalVars instead of [] ?
424 %length(Components,Len), print(nr_of_components_for_exists(Len,Components)),nl,
425 conjunct_components(Components,Parameters,Pred,Simplify).
426 % print('* EXISTS: '),translate:print_bexpr(Pred),nl.
427
428 :- use_module(bsyntaxtree).
429 conjunct_components(C,Parameters,Res,Simplify) :- conjunct_components2(C,Parameters,List,Simplify),
430 conjunct_predicates(List,Res).
431
432 conjunct_components2([],_Parameters,[],_). % :- (Parameters=[] -> true ; print(unused_existential_var(Parameters)),nl).
433 conjunct_components2([component(Pred,ComponentVars)|T],Parameters,Res,Simplify) :-
434 augment_ids(ComponentVars,CV), % translate atomic id names into b(identifier(...
435 tools:list_intersection(CV,Parameters,ExistParas),
436 %print(ord_inter(CV,Parameters,ExistParas)),nl,
437 (Simplify=true
438 -> create_and_simplify_exists(ExistParas,Pred,EP)
439 ; create_unsimplified_exists(ExistParas,Pred,EP)),
440 (EP=b(truth,pred,[]) % or should we allow _ for Info field ??
441 -> Res=TRes
442 ; Res=[EP|TRes]),
443 conjunct_components2(T,Parameters,TRes,Simplify).
444
445 % translate a list of atomic ides into ids with the b/3 wrapper
446 augment_ids([],[]).
447 augment_ids([ID|T],[b(identifier(ID),_,_)|TT]) :- augment_ids(T,TT).
448
449 extract_equalities(Parameters,Pred, ReducedParas,OutPred) :-
450 extract_equalities2(Pred,Parameters,ReducedParas,NewPred,[],Subst),
451 %sort(Subst,SortedSubst),
452 %print(extracted(Subst)),nl,
453 apply_subst(Subst,NewPred,OutPred).
454 % apply equality inside expressions: note: this may duplicate expressions !
455 % However, it also works if the equalities contain parameters
456
457 extract_equalities2(b(conjunct(A,B),pred,Info),Ids,RemIds,Res) --> !,
458 {Res=b(conjunct(PA,PB),pred,Info)},
459 extract_equalities2(A, Ids,RemIds1,PA),
460 extract_equalities2(B,RemIds1,RemIds,PB).
461 extract_equalities2(b(equal(ID,E),pred,_Info),Ids,RemIds,NewPred,InSubst,OutSubst) :-
462 same_id_is_member(ID,Ids,SID,RemIds),
463 %\+ occurs_in_expr(SID,E), % not cyclic; we check it again after applyng InSubst to E below
464 \+ in_subst_dom(SID,InSubst), % ensure we don't already have an equation for the same ID
465 compose_with_subst(InSubst,SID,E,OutSubst), % we can compose with existing substitution without occurs-check problem
466 always_well_defined_or_disprover_mode(E), % otherwise we may move evaluation of E forward !
467 !,
468 NewPred=b(truth,pred,[]).
469 extract_equalities2(X,Ids,Ids,X) --> [].
470
471 % compose a new binding Var/Expr with existing substitution Subst; where Subst was not yet applied to Expr
472 compose_with_subst(Subst,Var,Expr,[Var/NewExpr|ModSubst]) :-
473 apply_subst(Subst,Expr,NewExpr),
474 \+ occurs_in_expr(Var,NewExpr), % otherwise cyclic
475 apply_binding_to_substitution(Subst,Var,NewExpr,ModSubst).
476 apply_binding_to_substitution([],_,_,[]).
477 apply_binding_to_substitution([TV/TE|T],Var,Expr,[TV/TE2|NT]) :-
478 \+ occurs_in_expr(TV,Expr), % otherwise we get a cyclic substitution --> fail
479 replace_id_by_expr(TE,Var,Expr,TE2),
480 apply_binding_to_substitution(T,Var,Expr,NT).
481
482 apply_subst([],X,X).
483 apply_subst([Var/Expr|T],E,NewE) :-
484 (replace_id_by_expr(E,Var,Expr,E2) % replace identifier Var by expression Expr
485 -> %apply_to_tail(T,Var,Expr,NT), % apply binding to tail of substitution
486 apply_subst(T,E2,NewE)
487 ; add_internal_error('Call failed: ',apply_subst([Var/Expr|T],E,NewE)),NewE=E).
488
489
490 in_subst_dom(X,[I/_|T]) :- (X=I -> true ; in_subst_dom(X,T)).
491
492 same_id_is_member(X,[H|T],SID,Rest) :-
493 (same_id(X,H,SID) -> Rest=T ; Rest=[H|TR],same_id_is_member(X,T,SID,TR)).
494
495 % TO DO: investigate what is the exact difference with create_exists_opt
496 create_and_simplify_exists(Paras,Pred,ResultingPred) :-
497 % print(exists(Paras)),nl, translate:print_bexpr(Pred),nl,
498 extract_equalities(Paras,Pred,NewParas,NewPred),
499 % translate:print_bexpr(NewPred),nl,
500 %print(create_and_simplify_exists2(NewParas,NewPred)),nl,
501 create_and_simplify_exists2(NewParas,NewPred,ResultingPred).
502
503 %:- use_module(b_interpreter_check,[arithmetic_op/4]). % TODO REFACTOR and move this and code below to separate module
504 create_and_simplify_exists2([],Pred,R) :- !,R=Pred.
505 % simplify #ID.(ID:E1 & ID:E2) <=> E1 /\ E2 /= {} , etc... now done in ast_cleanup
506 % simplify #ID.(ID/:E) <=> E /= FullType also done in ast_cleanup
507 % simplify #SID.(SID > Expr) <=> truth now done in ast_cleanup
508 create_and_simplify_exists2(Parameters,Body,Res) :- create_unsimplified_exists(Parameters,Body,Res).
509 % TO DO: treat nested exists
510
511
512
513 :- use_module(b_ast_cleanup,[clean_up_pred/3]).
514 :- use_module(bsyntaxtree,[add_texpr_info_if_new/3]).
515 create_unsimplified_exists(Parameters,Body,Res) :-
516 % print(create_unsimplified_exists(Parameters,Body)),nl,
517 create_exists(Parameters,Body,Res1),
518 add_texpr_info_if_new(Res1,removed_typing,Res2), % unconditionally add this info, avoid generating warnings in exists_body_warning, see test 625
519 clean_up_pred(Res2,[],Res). % also computes used_ids,
520 % and performs some existential quantifier optimisations like replace_exists_by_not_empty
521 % previously removed_typing was only added for disjuncts or implications:
522 % (bsyntaxtree:is_a_disjunct_or_implication(Body,_,_,_)
523 % -> add_texpr_infos(Res2,[removed_typing],Res3) % avoid generating warnings in exists_body_warning; see, e.g., test 625
524 % ; Res3=Res2),