1 % (c) 2009-2026 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_ast_cleanup, [clean_up/3, clean_up_pred/3,
6 clean_up_pred_or_expr/3,
7 clean_up_pred_or_expr_with_path/4, % provide initial path, used for eventb context infos
8 clean_up_l_wo_optimizations/4,
9 clean_up_l_with_optimizations/4,
10 check_used_ids_info/4, recompute_used_ids_info/2,
11 definitely_not_empty_and_finite/1, definitely_infinite/1, % TO DO: move to another module
12 get_unique_id/2,
13 predicate_level_optimizations/2,
14 perform_do_not_enumerate_analysis/5,
15 has_top_level_wd_condition/1]).
16
17 :- use_module(module_information,[module_info/2]).
18 :- module_info(group,typechecker).
19 :- module_info(description,'This module implements transformations/simplifications on the AST.').
20
21 :- set_prolog_flag(double_quotes, codes). % relevant for detecting prob-ignore, test 2151 on SWI
22
23 :- use_module(tools, [safe_atom_chars/3,exact_member/2,foldl/4,filter/4]).
24 :- use_module(tools_lists, [length_less/2]).
25 :- use_module(error_manager).
26 :- use_module(debug).
27 :- use_module(self_check).
28 :- use_module(bsyntaxtree).
29 :- use_module(translate,[print_bexpr/1, translate_span/2, get_definition_context_from_span/2]).
30 :- use_module(btypechecker, [unify_types_strict/2]).
31 :- use_module(preferences,[get_preference/2]).
32 :- use_module(custom_explicit_sets,[convert_to_avl/2]).
33 :- use_module(prob_rewrite_rules(b_ast_cleanup_rewrite_rules),[rewrite_rule_with_rename/7]).
34 :- use_module(b_enumeration_order_analysis, [find_do_not_enumerate_variables/4]).
35 :- use_module(performance_messages,[perfmessage/2]).
36 :- use_module(b_operation_guards,[get_operation_propositional_guards/6]).
37
38 :- use_module(library(lists)).
39 :- use_module(library(ordsets)).
40 :- use_module(library(system), [environ/2]).
41
42 % entry point for cleaning up predicates; ensures that global, predicate-level optimizations also applied
43 clean_up_pred(Expr,NonGroundExceptions,CleanedUpExpr) :-
44 clean_up(Expr,NonGroundExceptions,CExpr),
45 (get_texpr_type(CExpr,pred)
46 -> predicate_level_optimizations(CExpr,CleanedUpExpr)
47 ; add_internal_error('Not predicate: ',clean_up_pred(Expr,NonGroundExceptions,CleanedUpExpr)),
48 CleanedUpExpr = CExpr).
49
50 % Warning: arguments swapped with clean_up for maplist !
51 clean_up_pred_or_expr(NonGroundExceptions,Expr,CleanedUpExpr) :-
52 ? clean_up_pred_or_expr_with_path(NonGroundExceptions,Expr,CleanedUpExpr,[]).
53 clean_up_pred_or_expr_with_path(NonGroundExceptions,Expr,CleanedUpExpr,Path) :-
54 (infer_seq_type_for_expr(Expr, Expr0) -> true ; Expr0 = Expr),
55 clean_up_init(NonGroundExceptions,Expr0,Expr1),
56 ? clean_up_aux(Expr1,NonGroundExceptions,CExpr,Path),
57 (get_texpr_type(CExpr,pred)
58 -> predicate_level_optimizations(CExpr,CleanedUpExpr,Path)
59 ; CleanedUpExpr = CExpr).
60
61 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62
63 % clean up some code afterwards
64 clean_up(Expr,NonGroundExceptions,CExpr) :- % clean-up with init
65 clean_up_init(NonGroundExceptions,Expr,Expr1),
66 clean_up_aux(Expr1,NonGroundExceptions,CExpr,[]).
67 clean_up_aux(Expr1,NonGroundExceptions,CExpr,Path) :- % performs no init
68 (preferences:get_preference(normalize_ast,true)
69 -> cleanups(normalize,Expr1,[],Expr2,Path)
70 ; Expr2=Expr1),
71 cleanups(pre,Expr2,[],TPExpr,Path),
72 remove_bt(TPExpr,PExpr,LExpr,TLExpr),
73 ? syntaxtransformation(PExpr,Subs,_,NewSubs,LExpr),
74 functor(PExpr,F,N),
75 % recursively clean up sub-expressions
76 ? clean_up_l(Subs,NonGroundExceptions,NewSubs,F/N,1,Path),
77 cleanups(post,TLExpr,[],CExpr,Path).
78 %, tools_printing:print_term_summary(cleaned_up(CExpr)),nl.
79
80 % just run post-phase
81 %cleanups_post(Expr,CleanedupExpr) :- cleanups(post,Expr,[],CleanedupExpr,[]).
82
83 clean_up_init(NonGroundExceptions,Expr,Expr2) :-
84 % ensure that WD info is available also for pre phase
85 % also pre-transform large set_extensions; they are only later transformed using eval_set_extension
86 ? (transform_bexpr_td_with_scoping(b_ast_cleanup:try_evaluate_set_extension,Expr,Expr1),
87 ? transform_bexpr(b_ast_cleanup:compute_wd_info_and_norm_record_types(NonGroundExceptions),Expr1,Expr2) -> true
88 ; add_internal_error('Call failed:',clean_up_init(NonGroundExceptions,Expr,_)), Expr2=Expr).
89
90
91 :- use_module(kernel_records,[normalise_record_types/4]).
92 :- use_module(bsyntaxtree,[transform_bexpr/3]).
93 compute_wd_info_and_norm_record_types(NonGroundExceptions,b(E,Type0,I),b(E,Type2,NInfo)) :-
94 ? (member(contains_wd_condition,I) -> NInfo=I
95 ? ; is_possibly_undefined(E)
96 -> NInfo = [contains_wd_condition|I]
97 ; NInfo = I
98 ),
99 (NonGroundExceptions = do_not_ground_types -> Type2=Type0
100 ; ground_type_to_any(Type0,NonGroundExceptions),
101 normalise_record_types(Type0,NonGroundExceptions,Type1,HasRecords),
102 % difference between Type1/0 in test 1906
103 (HasRecords == true,Type1 \= Type0 -> Type2=Type1 ; Type2=Type0)
104 ).
105
106 try_evaluate_set_extension(set_extension(List),Res,LocalIds) :-
107 (extension_should_be_evaluated(List),
108 evaluate_set_extension(List,EvaluatedList,LocalIds),
109 convert_to_avl(EvaluatedList,AVL)
110 % evaluate simple explicit set extensions: avoid storing & traversing position info & AST
111 -> (debug_mode(on) -> format('EVAL SET EXTENSION: ',[]), translate:print_bvalue(AVL),nl ; true),
112 Res=value(AVL)
113 ;
114 Res=set_extension(List) % avoid traversing inside set_extension, can be very large
115 ).
116 try_evaluate_set_extension(sequence_extension(List),Res,LocalIds) :-
117 (extension_should_be_evaluated(List),
118 evaluate_seq_extension_to_avl(List,AVL,LocalIds)
119 -> (debug_mode(on) -> format('EVAL SEQUENCE EXTENSION: ',[]), translate:print_bvalue(AVL),nl ; true),
120 Res=value(AVL)
121 ; Res=sequence_extension(List) % avoid traversing inside set_extension, can be very large
122 ).
123
124
125 :- use_module(library(ordsets),[ord_nonmember/2, ord_add_element/3]).
126 % apply the clean-up rules to an expression until all
127 % applicable rules are processed
128 % cleanups(Phase,Expr,AppliedRules,Result,Path):
129 % Phase: pre or post or normalize
130 % Expr: the expression to clean up
131 % AppliedRules: a sorted list of clean up rules that have been already applied
132 % and must only be apply once ("single" mode)
133 % Result: the cleaned-up expression
134 % Path: list of outer functors leading to this expression; can be used to decide about applicability of rules
135 cleanups(Phase,Expr,AppliedRules,Result,Path) :-
136 %% print(cleanups(Phase,Expr,AppliedRules,Result,Path)),nl,
137 % set up co-routines that ensure that "Rule" is not applied if
138 % is in the list AppliedRules
139 start_profile_rule(RuleInfos),
140 assure_single_rules(AppliedRules,Mode,Rule),
141 ? ( cleanup_phase(Phase,Expr,NExpr,Mode/Rule,Path) -> % try to apply a rule (matching the current phase)
142 ( Mode==single -> ord_add_element(AppliedRules,Rule,AppRules) % if the rule is marked as "single", we add to the list of already applied rules
143 ; Mode==multi -> AppRules = AppliedRules % if "multi", we do not add it to the list, the rule might be applied more than once
144 ; add_error_fail(b_ast_cleanup,'Unexpected rule mode ',Mode)
145 ),
146 stop_profile_rule(Rule,Mode,Phase,Expr,NExpr,RuleInfos),
147 %(NExpr=b(_,_,I),bsyntaxtree:check_infos(I,Rule) -> true ; true),
148 cleanups(Phase,NExpr,AppRules,Result,Path) % continue recursively with the new expression
149 ; % if no rule matches anymore,
150 Result = Expr, % we leave the expression unmodified
151 Mode=multi, Rule=none). % and unblock the co-routine (see assure_single_rules/3)
152
153
154 :- if(environ(prob_safe_mode,true)).
155 start_profile_rule([R1,W1]) :- statistics(runtime,[R1,_]),statistics(walltime,[W1,_]).
156 stop_profile_rule(Rule,Mode,Phase,Expr,_NExpr,[R1,W1]) :-
157 statistics(runtime,[R2,_]),statistics(walltime,[W2,_]), DeltaW is W2-W1, DeltaR is R2-R1,
158 (DeltaW < 20 -> true ; format('Firing AST cleanup rule ~w (mode:~w) in phase ~w took ~w ms (~w ms walltime)~n',[Rule,Mode,Phase,DeltaR,DeltaW]), translate:print_span(Expr),nl),
159 runtime_profiler:register_profiler_runtime(Rule,ast_cleanup,unknown,DeltaR,DeltaW).
160 % print(fired_rule(Rule,Mode,Phase)),nl, translate:print_bexpr_or_subst(Expr), print(' ===> '),nl, translate:print_bexpr_or_subst(NExpr),nl, print_ast(_NExpr),nl. %% COMMENT IN TO SEE applied RULES <---------------
161 %(map_over_typed_bexpr(b_ast_cleanup:check_valid_result,NExpr) -> true ; true), % comment in to check output after every firing of a rule
162 :- else.
163 start_profile_rule(_).
164 stop_profile_rule(_,_,_,_,_,_).
165 :- endif.
166
167 %check_valid_result(b(xexists([b(identifier(msgXX),integer,_)|_],_),pred,Infos)) :- nonmember(allow_to_lift_exists,Infos),print(missing_info),nl,trace,fail.
168
169 assure_single_rules([],_Mode,_Rule) :- !.
170 assure_single_rules(AppliedRules,Mode,Rule) :-
171 assure_single_rules2(AppliedRules,Mode,Rule).
172 :- block assure_single_rules2(?,-,?),assure_single_rules2(?,?,-).
173 assure_single_rules2(_AppliedRules,_,none) :- !.
174 assure_single_rules2(_AppliedRules,multi,_) :- !.
175 assure_single_rules2(AppliedRules,_,Rule) :-
176 % typically AppliedRules not very long; would also do: \+ member(Rule, AppliedRules).
177 ord_nonmember(Rule,AppliedRules).
178
179 cleanup_phase(Phase,OTExpr,NTExpr,Mode/Rule,Path) :-
180 create_texpr(OExpr,OType,OInfo,OTExpr),
181 check_generated_info(OInfo,entry,Path),
182 create_texpr(NExpr,NType,NInfo,NTExpr),
183 ? cleanup_phase2(Phase,OExpr,OType,OInfo,NExpr,NType,NInfo,Mode/Rule,Path),
184 check_generated_info(NInfo,Rule,Path).
185 cleanup_phase2(normalize,OExpr,OType,OInfo,NExpr,NType,NInfo,Mode/Rule,_Path) :-
186 decompose_rule(Mode_Rule,Mode,Rule),
187 cleanup_normalize(OExpr,OType,OInfo,NExpr,NType,NInfo,Mode_Rule),
188 (debug_mode(off) -> true
189 ; print('Rewritten: '), print_bexpr(b(OExpr,OType,OInfo)),nl,
190 print(' Into: '), print_bexpr(b(NExpr,NType,NInfo)),nl
191 ).
192 cleanup_phase2(pre,OExpr,OType,OInfo,NExpr,NType,NInfo,Mode/Rule,Path) :-
193 decompose_rule(Mode_Rule,Mode,Rule),
194 ? cleanup_pre_with_path(OExpr,OType,OInfo,NExpr,NType,NInfo,Mode_Rule,Path).
195 cleanup_phase2(post,OExpr,OType,OInfo,NExpr,NType,NInfo,Mode/Rule,Path) :-
196 decompose_rule(Mode_Rule,Mode,Rule),
197 ? cleanup_post_with_path(OExpr,OType,OInfo,NExpr,NType,NInfo,Mode_Rule,Path).
198 % check_ast(b(NExpr,NType,NInfo)),nl.
199
200 :- block decompose_rule(-,?,?).
201 decompose_rule(Mode_Rule,Mode,Rule) :-
202 (functor(Mode_Rule,'/',2)
203 -> Mode_Rule = Mode/Rule
204 ; add_internal_error('Illegal cleanup rule, missing mode: ',Mode_Rule),fail).
205
206 check_generated_info(Info,Rule,Path) :-
207 get_preference(prob_safe_mode,true),
208 (select(used_ids(_),Info,I1) -> member(used_ids(_),I1)),!,
209 format('Illegal used ids generated by ~w within ~w~n Infos=~w~n',[Rule,Path,Info]),
210 add_internal_error('Illegal Info generated by rule: ',Rule),
211 fail.
212 check_generated_info(_,_,_).
213
214 clean_up_l_wo_optimizations(Rest,NonGroundExceptions,CRest,SectionName) :-
215 maplist(clean_up_init(NonGroundExceptions),Rest,Rest1),
216 clean_up_l(Rest1,NonGroundExceptions,CRest,top_level(SectionName),1,[]).
217 clean_up_l([],_,[],_Functor,_Nr,_Path).
218 clean_up_l([Expr|Rest],NonGroundExceptions,[CExpr|CRest],Functor,ArgNr,Path) :-
219 ? clean_up_aux(Expr,NonGroundExceptions,CExpr,[path_arg(Functor,ArgNr)|Path]),
220 A1 is ArgNr+1,
221 ? clean_up_l(Rest,NonGroundExceptions,CRest,Functor,A1,Path).
222
223
224 % MAIN ENTRY POINT for b_machine_construction, bmachine_eventb, proz
225 % same as clean_up_l but also applies predicate_level_optimizations
226 % Context is just the name of the section/context in which the optimizations are run
227 clean_up_l_with_optimizations(Rest,NonGroundExceptions,CRest,Context) :-
228 %clean_up_l(Rest,NonGroundExceptions,CRest,top_level,1,[]).
229 ? (clean_up_l_with_opt(Rest,NonGroundExceptions,CleanedUpRest,top_level(Context),1,[]) -> CRest=CleanedUpRest
230 ; add_internal_error('Call failed:',clean_up_l_with_optimizations(Rest,NonGroundExceptions,CRest,Context)),
231 CRest=Rest
232 ).
233 clean_up_l_with_opt([],_,[],_Functor,_Nr,_Path).
234 clean_up_l_with_opt([Expr|Rest],NonGroundExceptions,[CExpr|CRest],Functor,ArgNr,Path) :-
235 %print('Cleaning up: '),translate:print_bexpr_or_subst(Expr),nl,
236 clean_up_init(NonGroundExceptions,Expr,Expr1),
237 ? clean_up_pred_or_expr_with_path(NonGroundExceptions,Expr1,CExpr,[path_arg(Functor,ArgNr)|Path]),
238 A1 is ArgNr+1,
239 ? clean_up_l_with_opt(Rest,NonGroundExceptions,CRest,Functor,A1,Path).
240
241 :- use_module(specfile,[animation_mode/1, animation_minor_mode/1]).
242 % cleanup_pre(OldExpr,OldType,OldInfo,NewExpr,NewType,NewInfo,Mode/Rule)
243
244 % optional normalization rules
245 % These rules are now generated using the prob_rule_compiler
246 %cleanup_normalize(empty_sequence,Type,Info,empty_set,Type,Info, multi/apply_normalization_rule(empty_sequence)). % rule probably not useful as empty_sequence converted to value([])?
247 cleanup_normalize(Expr,Type,Info,NewExpr,NewType,NewInfo, multi/apply_normalization_rule(Rule)) :-
248 b_ast_cleanup_rewrite_rules:normalization_rule_with_rename(Expr,Type,Info,NewExpr,NewType,NewInfo,Rule),
249 (debug_mode(off) -> true
250 ; format('Use rewrite_rule_normalize ~w~n',[Rule]),
251 print_bexpr(b(NewExpr,NewType,NewInfo)),nl
252 ),
253 (ground(NewExpr) -> true
254 ; print(not_ground_rewrite(Rule,Type,Info)),nl,
255 write(Expr),nl, write(' --> '),nl, write(NewExpr),nl,
256 fail).
257
258 never_transform_or_optimise(boolean_false).
259 never_transform_or_optimise(boolean_true).
260 %never_transform_or_optimise(bool_set).
261 never_transform_or_optimise(empty_set).
262 never_transform_or_optimise(empty_sequence). % except for normalization (cf above)
263 never_transform_or_optimise(falsity).
264 %never_transform_or_optimise(max_int).
265 %never_transform_or_optimise(min_int).
266 never_transform_or_optimise(truth).
267 never_transform_or_optimise(identifier(_)).
268 never_transform_or_optimise(integer(_)).
269 never_transform_or_optimise(real(_)).
270 never_transform_or_optimise(string(_)).
271 never_transform_or_optimise(value(_)) :- preferences:preference(normalize_ast, false).
272
273
274 % first check for a few expressions that never need to be optimised, rewritten:
275 cleanup_pre_with_path(E,_,_,_,_,_,_,_) :- never_transform_or_optimise(E),!,fail.
276 % TO DO: think about enabling the following clause
277 cleanup_pre_with_path(exists(AllIds,P),pred,I,exists(AllIds,P),pred,NewI,single/annotate_toplevel_exists,Path) :-
278 % mark the exists inside {paras| #(AllIds).(P)} as allowed to be lifted; relevant for test 1945 (although delayed semi_lifting in b_test_exists_wo_expansion also solves performance issue)
279 Path = [H|_], % TODO: also deal with lambda and other quantifications
280 H=path_arg(comprehension_set/2,1),
281 % % TO DO: add some conditions under which we allow to lift
282 get_preference(data_validation_mode,true), % TODO: check if we cannot enable this more generally
283 (debug_mode(off) -> true ; add_message(b_ast_cleanup,'Marking exists for lifting: ',AllIds,I)),
284 add_info_if_new(I,allow_to_lift_exists,NewI).
285 % mark existential quantifier as outermost: no need to delay it in b_interpreter:b_test_exists_wo_expansion
286 cleanup_pre_with_path(OExpr,OType,OInfo,NExpr,NType,NInfo,Mode_Rule,_) :-
287 ? cleanup_pre(OExpr,OType,OInfo,NExpr,NType,NInfo,Mode_Rule).
288
289 % Now cleanup_pre rules that do not require path:
290
291 cleanup_pre(block(TS),subst,_,Subst,subst,Info,multi/remove_block) :-
292 !,get_texpr_expr(TS,Subst),get_texpr_info(TS,Info).
293 % replace finite by truth
294 cleanup_pre(finite(S),pred,I,truth,pred,[was(finite(S))|I],multi/remove_finite) :-
295 % preferences:get_preference(disprover_mode,false), % keep finite in disprover goals
296 get_texpr_type(S,Type),
297 (typing_tools:is_provably_finite_type(Type) -> true
298 % ; is_infinite_ground_type(Type) -> fail
299 % ; print(no_longer_assuming_finite(Type)),nl,b_global_sets:portray_global_sets,nl,fail
300 ),!. % add_message(ast,'Removed : ',b(finite(S),pred,I),I).
301 /*
302 cleanup_pre(domain(SETC),Type,I,
303 comprehension_set(DomainIds,NewPred),Type,I,multi/dom_let_pred) :-
304 get_texpr_expr(SETC,comprehension_set(CompIds,CompPred)),
305 get_domain_range_ids(CompIds,DomainIds,[RangeId]),
306 get_texpr_ids(CompIds,UnsortedIds), sort(UnsortedIds,Blacklist),
307 conjunction_to_list(CompPred,Preds),
308 select_equality(TId,Preds,Blacklist,Expr,Rest,_UsedIds,check_well_definedness),
309 same_id(TId,RangeId,_),
310 !,
311 LetIds = [RangeId], Exprs = [Expr],
312 conjunct_predicates(Rest,RestPred),
313 NewPred = b(let_predicate(LetIds,Exprs,RestPred),pred,[generated(domain)]),
314 print('Translated dom({...,x|x=E&...}) into: '),
315 print_bexpr(b(comprehension_set(DomainIds,NewPred),Type,[])),nl.
316 */
317 /* strangely enough: this does not seem to buy anything:
318 cleanup_pre(domain(SETC),Type,I,
319 let_expression(LetIds,Exprs,NewExpr),Type,[generated(domain)|I],multi/dom_let_expr) :-
320 get_texpr_expr(SETC,comprehension_set(CompIds,CompPred)),
321 get_domain_range_ids(CompIds,DomainIds,[RangeId]),
322 get_texpr_ids(CompIds,UnsortedIds), sort(UnsortedIds,Blacklist),
323 conjunction_to_list(CompPred,Preds),
324 select_equality(TId,Preds,Blacklist,Expr,Rest,_UsedIds,check_well_definedness),
325 same_id(TId,RangeId,_),
326 !,
327 LetIds = [RangeId], Exprs = [Expr],
328 conjunct_predicates(Rest,RestPred),
329 get_texpr_info(SETC,CInfo),
330 NewExpr = b(comprehension_set(DomainIds,RestPred),Type,CInfo),
331 print('Translated dom({...,x|x=E&...}) into: '), print_bexpr(b(let_expression(LetIds,Exprs,NewExpr),Type,[])),nl.
332 */
333 % exchange quantified union by generalized union
334 cleanup_pre(QUANT,Type,I,
335 let_expression(LetIds,Exprs,NewExpr),Type,[generated(QuantOP)|I],multi/quant_union_inter_let) :-
336 get_preference(optimize_ast,true),
337 quantified_set_operator(QUANT,QuantOP,AllIds,Pred,Expr),
338 conjunction_to_nontyping_list(Pred,Preds),
339 % The ids are needed to build a "black list"
340 get_sorted_ids(AllIds,BlackListIds),
341 find_one_point_rules(AllIds,Preds,BlackListIds,LetIds,Exprs,RestIds,NewPreds),
342 % only succeed if we found at least one id which can be rewritten as let
343 LetIds = [_ID1|_],
344 !,
345 construct_quantified_set_operator(QuantOP,RestIds,NewPreds,Expr,Type,I, NewExpr),
346 (debug_mode(off) -> true
347 ; print('Translated UNION/INTER into: '), print_bexpr(b(let_expression(LetIds,Exprs,NewExpr),Type,[])),nl).
348 cleanup_pre(quantified_union(Ids,Pred,Expr),Type,I,DomCOMPSET,Type,I,multi/quant_union_into_comprehension) :-
349 singleton_set_extension(Expr,CoupleExpr),
350 nested_couple_to_list(CoupleExpr,List), % we have UNION(..).(...| { A |-> B }) --> List = [A,B]
351 match_ids(List,Ids,AllIds,RestIds),
352 (RestIds = []
353 -> true % UNION(x,y).(x:INTEGER & y=x+1|{x|->y}) --> {x,y| x:INTEGER & y=x+1} = %x.(...).
354 ; % UNION(y,x,z).(x:INTEGER & y=x+1|{x|->y}) --> dom({x,y,z| x:INTEGER & y=x+1})
355 % used to fail test 1101; check on List relevant for test 2359
356 List = [_,_|_] % at least two ids, otherwise use quant_union translation below: ran( %(Ids).(Pred|One) )
357 ),
358 COMPSET = comprehension_set(AllIds,Pred),
359 generate_dom_for_ids(RestIds,COMPSET,Type,I,TDomCOMPSET), % add one dom(.) construct per ID that is projected away
360 (debug_mode(off) -> true
361 ; print('Translated UNION over identifier couples into: '), print_bexpr(TDomCOMPSET),nl),
362 % check_ast(TDomCOMPSET), will complain about redundant typing as we are still in pre phase
363 TDomCOMPSET = b(DomCOMPSET,Type,_).
364 % disabled following rule for the moment: slows down setup_constants in B/PerformanceTests/AVL_Index_Test.mch
365 %cleanup_pre(QUANT,Type,I, NewExpr,Type,[generated(QuantOP)|I],single/quant_union_inter_let_body) :-
366 % get_preference(optimize_ast,true),
367 % quantified_set_operator(QUANT,QuantOP,AllIds,Pred,Expr),
368 % % Detect an equation LetId = EqExpr so that LetId only used in expression part of QUANT
369 % % UNION(x,y).(y:9..10 & x=2**y|{y|->x}) ---> UNION(y).(y:9..10| LET x BE x=2**y IN {y|->x} END)
370 % % this is not covered by find_one_point_rules
371 % % also: as EqExpr is moved later, there is no issue with well-definedness
372 % % TODO: enable rule as multi or iterate until all LetIds identifier, avoid re-computing find_identifier_uses repeatedly, maybe merge with other QUANT rules
373 % find_identifier_uses_if_necessary(Expr,[],ExprUsedIds),
374 % get_sorted_ids(AllIds,SAllIds),
375 % ord_intersection(SAllIds,ExprUsedIds,Candidates),
376 % Candidates \= [],
377 % conjunction_to_nontyping_list(Pred,Preds),
378 % select(TEqual,Preds,RestPreds),
379 % is_equality(TEqual,TA,TB),
380 % ( get_texpr_id(TA,LetId),TB=EqExpr ; get_texpr_id(TB,LetId),TA=EqExpr ),
381 % ord_member(LetId,Candidates),
382 % find_identifier_uses_if_necessary(EqExpr,[],EqUsedIds),
383 % ord_nonmember(LetId,EqUsedIds), % no recursive equation x=x+1
384 % find_identifier_uses_l(RestPreds,[],RestPredIds),
385 % ord_nonmember(LetId,RestPredIds),
386 % select(TID,AllIds,RestIds), get_texpr_id(TID,LetId),
387 % LetBodyTExpr = b(let_expression([TID],[EqExpr],Expr),Type,[]),
388 % construct_quantified_set_operator(QuantOP,RestIds,RestPreds,LetBodyTExpr,Type,I, NewTExpr),
389 % get_texpr_expr(NewTExpr,NewExpr),
390 % (debug_mode(off) -> true
391 % ; print('Moved equality of UNION/INTER into body: '), print_bexpr(NewTExpr),nl).
392 cleanup_pre(quantified_union(Ids,Pred,Expr),Type,I,Res,Type,[was(quantified_union)|I],multi/quant_union_symbolic) :-
393 memberchk(prob_annotation('SYMBOLIC'),I),
394 % UNION(Ids).(Pred|Expr) --> {u| #Ids.(Pred & u:Expr)}
395 get_texpr_set_type(Expr,IdType),
396 !,
397 get_unique_id_inside('__UNION__',Pred,Expr,FRESHID),
398 NewTID = b(identifier(FRESHID),IdType,[]),
399 safe_create_texpr(member(NewTID,Expr),pred,Member),
400 create_exists_opt(Ids,[Pred,Member],Exists),
401 Res = comprehension_set([NewTID],Exists),
402 (debug_mode(off) -> true ; print('UNION @symbolic: '), print_bexpr(b(Res,Type,I)),nl).
403 % exchange quantified union by generalized union
404 cleanup_pre(quantified_union(Ids,Pred,Expr),Type,I,Res,Type,I,multi/quant_union) :-
405 !,
406 ((get_preference(convert_comprehension_sets_into_closures,false), % for test 1101: UNION is a form of let and forces computation at the moment; translating it into a construct without union means that it may be kept symbolic
407 singleton_set_extension(Expr,One) %, debug:bisect(Expr,[1,1,1,1,1,1,1])) % [1,1,1,1,1,1,1] slow; 1,1,0 1,1,1,0 fast
408 )
409 % UNION(Ids).(Pred|{One}) --> ran( %(Ids).(Pred|One) )
410 -> quantified_set_op(Ids,Pred,One,quantified_union,I,TRes),
411 get_texpr_expr(TRes,Res),
412 (debug_mode(off) -> true ; print('UNION - SINGLETON: '), print_bexpr(TRes),nl)
413 ;
414 % UNION(Ids).(Pred|Expr) --> union( ran( %(Ids).(Pred|Expr) ) )
415 quantified_set_op(Ids,Pred,Expr,quantified_union,I,Set),
416 Res = general_union(Set)
417 ).
418 %alternate encoding:
419 %cleanup_pre(quantified_union(Ids,Pred,Expr),Type,I,Set,Type,I,multi/quant_union) :-
420 %poses problem to test 614, 1101
421 % !,quantified_union_op(Ids,Pred,Expr,Type,Set).
422 % exchange quantified intersection by generalized intersection
423 cleanup_pre(quantified_intersection(Ids,Pred,Expr),Type,I,general_intersection(Set),Type,I,multi/quant_inter) :-
424 !,quantified_set_op(Ids,Pred,Expr,quantified_intersection,I,Set).
425 cleanup_pre(function(FUN,Argument),Type,I,Result,Type,I2,RULE) :-
426 ? cleanup_pre_function(FUN,Argument,Type,I,Result,I2,RULE).
427 cleanup_pre(first_projection(A,B),Type,I,Set,Type,[was(prj1),prob_annotation('SYMBOLIC')|I],multi/first_projection) :-
428 !,create_projection_set(A,B,first,Set).
429 cleanup_pre(second_projection(A,B),Type,I,Set,Type,[was(prj2),prob_annotation('SYMBOLIC')|I],multi/second_projection) :-
430 !,create_projection_set(A,B,second,Set).
431 cleanup_pre(event_b_first_projection(Rel),Type,I,Set,Type,[prob_annotation('SYMBOLIC')|I],multi/ev_first_projection) :-
432 !,create_event_b_projection_set(Rel,first,Set).
433 cleanup_pre(event_b_second_projection(Rel),Type,I,Set,Type,[prob_annotation('SYMBOLIC')|I],multi/ev_second_projection) :-
434 !,create_event_b_projection_set(Rel,second,Set).
435 cleanup_pre(event_b_first_projection_v2,Type,I,Set,Type,[prob_annotation('SYMBOLIC')|I],multi/ev2_first_projection) :-
436 !,create_event_b_projection_set_v2(Type,first,Set).
437 cleanup_pre(event_b_second_projection_v2,Type,I,Set,Type,[prob_annotation('SYMBOLIC')|I],multi/ev2_second_projection) :-
438 !,create_event_b_projection_set_v2(Type,second,Set).
439 cleanup_pre(image(Fun,SONE),T,I,Res,T,I,multi/succ_pred_image_optimisation) :-
440 singleton_set_extension(SONE,One), % succ[{h}] --> {h+1} and pred[{h}] --> {h-1} ; useful for alloy2b
441 precompute_pred_succ_function_call(Fun,One,FunOneRes),
442 !,
443 Res = set_extension([b(FunOneRes,integer,I)]).
444 cleanup_pre(union(Lambda1,Lambda2),T,I,lambda(TIDs1,TPred,TVal),T,I,multi/combine_lambdas_if_then_else) :-
445 Lambda1 = b(lambda(TIDs1,TPred1,TValue1),_,_), get_texpr_ids(TIDs1,Ids),
446 Lambda2 = b(lambda(TIDs2,TPred2,TValue2),_,_), get_texpr_ids(TIDs2,Ids),
447 % %x.(P & Cond1 | Val1) \/ %x.(P & not(Cond1) | Val2) ==> %x.(P| IF Cond1 THEN Val1 ELSE Val2 END)
448 conjunction_to_list(TPred1,L1),
449 conjunction_to_list(TPred2,L2),
450 append(Front1,[Last1],L1),
451 append(Front2,[Last2],L2),
452 maplist(same_texpr,Front1,Front2),
453 is_negation_of(Last1,Last2),
454 conjunct_predicates_with_pos_info(Front1,TPred),
455 get_texpr_type(TValue1,TV),
456 safe_create_texpr(if_then_else(Last1,TValue1,TValue2),TV,[],TVal),
457 (debug_mode(off) -> true
458 ; format('Union of lambdas converted to if-then-else: ',[]),
459 translate:print_bexpr(b(lambda(TIDs1,TPred,TVal),T,I)),nl
460 ).
461 cleanup_pre(event_b_comprehension_set(Ids,Expr,Pred),T,I,NewExpression,T,
462 [was(event_b_comprehension_set)|I],multi/ev_compset) :-
463 rewrite_event_b_comprehension_set(Ids,Expr,Pred, T, NewExpression).
464 cleanup_pre(domain_restriction(A,B),T,I,identity(A),T,I,multi/event_b_to_normal_identity1) :-
465 /* translate S <| id into id(S) */
466 is_event_b_identity(B).
467 cleanup_pre(range_restriction(B,A),T,I,identity(A),T,I,multi/event_b_to_normal_identity2) :-
468 /* translate id |> S into id(S) */
469 is_event_b_identity(B).
470 % what about translating id(TOTAL TYPE) into event_b_identity ??
471 cleanup_pre(ring(A,B),T,I,composition(B,A),T,I,multi/ring_composition). % replace backward composition by forward compositoin
472 cleanup_pre(less_equal(A,B),pred,I,less_equal_real(A,B),pred,I,multi/remove_ambiguous_leq_real) :-
473 get_texpr_type(A,real).
474 cleanup_pre(greater_equal(A,B),pred,I,less_equal_real(B,A),pred,I,multi/remove_ambiguous_geq_real) :-
475 get_texpr_type(A,real).
476 cleanup_pre(less(A,B),pred,I,less_real(A,B),pred,I,multi/remove_ambiguous_lt_real) :-
477 get_texpr_type(A,real).
478 cleanup_pre(greater(A,B),pred,I,less_real(B,A),pred,I,multi/remove_ambiguous_gt_real) :-
479 get_texpr_type(A,real).
480 cleanup_pre(unary_minus(A),real,I,unary_minus_real(A),real,I,multi/remove_ambiguous_unary_minus_real).
481 cleanup_pre(power_of(A,B),real,I,power_of_real(A,TB),real,I,multi/convert_into_power_of_real) :-
482 get_texpr_type(A,real),
483 safe_create_texpr(convert_real(B),real,[],TB). % TO DO: add pos; will call real_power_of_wf
484 cleanup_pre(max(A),real,I,max_real(A),real,I,multi/remove_ambiguous_unary_max_real). % dispatches to real_maximum_of_set
485 cleanup_pre(min(A),real,I,min_real(A),real,I,multi/remove_ambiguous_unary_min_real). % and to real_minimum_of_set
486 cleanup_pre(add(A,B),real,I,add_real(A,B),real,I,multi/remove_ambiguous_add_real).
487 cleanup_pre(div(A,B),real,I,div_real(A,B),real,I,multi/remove_ambiguous_div_real).
488
489 cleanup_pre(minus_or_set_subtract(A,B),integer,I,minus(A,B),integer,I,multi/remove_ambiguous_minus_int).
490 cleanup_pre(minus_or_set_subtract(A,B),real,I,minus_real(A,B),real,I,multi/remove_ambiguous_minus_real).
491 cleanup_pre(minus_or_set_subtract(A,B),Type,I,set_subtraction(A,B),Type,I,multi/remove_abiguous_minus_set) :-
492 is_set_type(Type,_).
493 cleanup_pre(mult_or_cart(A,B),integer,I,multiplication(A,B),integer,I,multi/remove_ambiguous_times_int).
494 cleanup_pre(mult_or_cart(A,B),real,I,multiplication_real(A,B),real,I,multi/remove_ambiguous_times_real).
495 cleanup_pre(mult_or_cart(A,B),Type,I,cartesian_product(A,B),Type,I,multi/remove_ambiguous_times_set) :-
496 is_set_type(Type,_).
497 cleanup_pre(E,T,Iin,E,T,Iout,multi/remove_rodinpos) :- % we use multi but can per construction only be applied once
498 selectchk(nodeid(rodinpos(_,[],_)),Iin,Iout). % remove rodinpos information with Name=[]
499 cleanup_pre(partition(X,[Set]),pred,I,equal(X,Set),pred,I,multi/remove_partition_one_element) :-
500 !,
501 % partition(X,Set) <=> X=Set
502 (debug_mode(off) -> true ;
503 print('Introducing equality for partition: '),
504 print_bexpr(X), print(' = '), print_bexpr(Set),nl).
505 cleanup_pre(case(Expression,CASES,Else),subst,I,NewSubst,subst,I,single/rewrite_case_to_if_then_else) :-
506 % translate CASE E OF EITHER e1 THEN ... ---> LET case_expr BE case_expr=E IN IF case_expr=e1 THEN ...
507 get_texpr_type(Expression,EType), get_texpr_info(Expression,EInfo),
508 ExprID = b(identifier(EID),EType,EInfo),
509 (get_texpr_id(Expression,EID)
510 -> NewSubst = if(IFLISTE) % no LET necessary
511 ; NewSubst = let([ExprID],Equal,b(if(IFLISTE),subst,I)),
512 get_unique_id_inside('case_expr',b(case(Expression,CASES,Else),subst,I),EID),
513 safe_create_texpr(equal(ExprID,Expression),pred,Equal)
514 ),
515 (maplist(gen_if_elsif(ExprID),CASES,IFLIST)
516 -> (get_texpr_expr(Else,skip) -> IFLISTE = IFLIST
517 ; TRUTH = b(truth,pred,[]), get_texpr_info(Else,EI),
518 append(IFLIST,[b(if_elsif(TRUTH,Else),subst,EI)],IFLISTE)
519 ),
520 (debug_mode(off) -> true
521 ; print('Translating CASE to IF-THEN-ELSE: '), print_bexpr(Expression),nl
522 %,translate:print_subst(b(NewSubst,subst,[])),nl
523 )
524 ; add_internal_error('Translation of CASE to IF-THEN-ELSE failed: ',CASES),fail
525 ).
526 cleanup_pre(exists(AllIds,Body),pred,I,exists(AllIds,Body),pred,I,single/check_implication_inside_exists) :-
527 is_an_implication(Body,LHS,_RHS),
528 % #x.(P => Q) is true if for some x P is false; usually one intends to write #x.(P & Q)
529 % see tests 1452, 1453 and 1493; Note: typing is always added by Rodin anyway
530 not_generated_exists_paras(AllIds),
531 Ex = b(exists(AllIds,Body),pred,I),
532 ( is_typing_predicate(LHS) % a typing predicate is equivalent to btrue; btrue => RHS is not problematic
533 -> true
534 ; animation_minor_mode(X),(X=eventb ; X=tla)
535 -> add_warning(bmachine_static_checks,'Body of existential quantifier is an implication: ',Ex,I)
536 ; member(removed_typing,I) % we have #x.(TYPE & (LHS => RHS)) and exists_body_warning has not triggered
537 -> add_warning(bmachine_static_checks,
538 'Body of existential quantifier corresponds to an implication (after removing typing predicates): ',Ex,I)
539 ; true % bmachine_construction:exists_body_warning already triggered and generated a warning already
540 ).
541 cleanup_pre(exists(AllIds,Body),pred,I0,NewP1,pred,NewI,single/components_partition_exists) :-
542 get_preference(optimize_ast,true),
543 nonmember(partitioned_exists,I0), % avoid re-computing components on something that is already partitioned
544 Simplify=no_cleanup_and_simplify, % avoid loops, but does not call extract_equalities_in_quantifier
545 % Warning: the next call may make use of existing used_ids infos; if they are wrong we may have a problem!
546 b_interpreter_components:construct_optimized_exists(AllIds,Body,NewPred,Simplify,NrC),
547 (NrC = 1 % just one component; perform no change to avoid re-ordering ids, ... ; see test 510
548 -> NewP1=exists(AllIds,Body), NewI=I0
549 ; get_texpr_ids(AllIds,Ids), sort(Ids,SortedIds),
550 add_important_infos_to_exists_conjuncts(NewPred,I0,SortedIds, b(NewP1,pred,NewI)),
551 % important e.g. for test 1945; in particular allow_to_lift_exists
552 % Note: construct_optimized_exists can lift unrelated inner exists out (/ClearSy/2023/perf_0704/rule_genz.mch)
553 (debug_mode(on), NewP1 \= exists(_,_)
554 -> format('PARTITIONED EXISTS:~n ',[]), translate:nested_print_bexpr(b(NewP1,pred,NewI)),nl ; true)
555 ).
556 %cleanup_pre(exists(AllIds,Pred),pred,I,NewP1,pred,I,single/extract_equalities) :-
557 % b_interpreter_components:extract_equalities_in_quantifier(AllIds,Pred,NewParas,NewPred),
558 % (NewParas=[] -> get_texpr_expr(NewPred,NewP1)
559 % ; NewP1 = exists(NewParas,NewPred)),
560 % (debug_mode(_on), NewParas \= AllIds
561 % -> format('Inlined equalities in EXISTS:~n ',[]), translate:nested_print_bexpr(b(NewP1,pred,I)),nl ; true).
562 cleanup_pre(exists(AllIds,P),pred,I,NewP1,pred,I,single/factor_out) :-
563 % not REDUNDANT with rule above: Simplify=no_cleanup_and_simplify used above!
564 conjunction_to_nontyping_list(P,Preds),
565 % move things which do not depend on AllIds outside
566 % transform, e.g., #(x).(y>2 & x=y) --> y>2 & #(x).(x=y)
567 get_preference(optimize_ast,true),
568 create_exists_opt(AllIds,Preds,b(NewP1,pred,_I),Modified),
569 %(Modified = true -> print(exists(AllIds)),nl,print_bexpr(b(NewP1,pred,_I)),nl),
570 % the rule will fire again on the newly generated sub predicate ! -> fix ?
571 Modified=true.% check if anything modified; otherwise don't fire rule
572
573 cleanup_pre(exists(AllIds,P),pred,Info0,NewP,pred,INew,multi/remove_single_use_equality) :-
574 % remove existentially quantified variables which are defined by an equation and are used only once
575 % e.g., Z1...Z4 in not(#(X,Y,Z,Z1,Z2,Z3,Z4).(X:INTEGER & X*Y=Z1 & Z1*Z = Z2 & Z*X = Z3 & Z3*Y = Z4 & Z2 /= Z4))
576 get_preference(optimize_ast,true),
577 (length_less(AllIds,100) -> true % otherwise the code becomes quite inefficient at the moment
578 ; perfmessage('Large existential quantifier, performing limited optimizations',Info0),
579 fail),
580 conjunction_to_list(P,Preds),
581 CheckWellDef=no_check,
582 ? select_equality(TId,Preds,[],_,IDEXPR,RestPreds,_,CheckWellDef),
583 get_texpr_id(TId,ID),
584 ? select(TIdE,AllIds,RestIds), get_texpr_id(TIdE,ID),
585 can_be_optimized_away(TIdE),
586 \+ occurs_in_expr(ID,IDEXPR), % we cannot inline #x.(x=y+x & ...)
587 always_defined_full_check_or_disprover_mode(IDEXPR), % otherwise we may remove WD issue by removing ID if Count=0 or move earlier/later if count=1
588 single_usage_identifier(ID,RestPreds,Count), % we could also remove if Expr is simple
589 (Count=0 -> debug_println(19,unused_equality_id(ID)),
590 PL=RestPreds
591 ; % Count should be 1
592 conjunct_predicates_with_pos_info(RestPreds,RestPred),
593 replace_id_by_expr(RestPred,ID,IDEXPR,E2),
594 conjunction_to_list(E2,PL)
595 ),
596 create_exists_opt(RestIds,PL,TNewP), % no need to computed used ids yet; we could do this:
597 %conjunct_predicates_with_pos_info(PL,PP), safe_create_texpr(exists(RestIds,PP),pred,TNewP),
598 (debug_mode(off) -> true
599 ; format('Remove existentially quantified identifier with single usage: ~w (count: ~w)~n',[ID,Count]), print_bexpr(IDEXPR),nl),
600 TNewP = b(NewP,pred,INew),!.
601 cleanup_pre(exists(AllIds,P),pred,I,let_predicate(LetIds,Exprs,NewP),pred,INew,multi/exists_to_let) :-
602 % rewrite predicates of the form #x.(x=E & P(x)) into (LET x==E IN P(x))
603 % side condition for #(ids).(id=E & P(ids)): no identifiers of ids occur in E
604 get_preference(optimize_ast,true),
605 conjunction_to_nontyping_list(P,Preds), % TO DO: avoid recomputing again (see line in clause above)
606 % The ids are needed to build a "black list"
607 get_sorted_ids(AllIds,Ids),
608 find_one_point_rules(AllIds,Preds,Ids,LetIds,Exprs,RestIds,NewPreds),
609 % no_check is not ok in the context of existential quantification and reification:
610 % #x.(1:dom(f) & x=f(1) & P) --> LET x=f(1) IN 1:dom(f) & P END
611 % it is not ok if the whole predicate gets reified in b_intepreter_check !! Hence we use always_defined_full_check_or_disprover_mode; see Well_def_1.9.0_b5 in private_examples
612 % only succeed if we found at least one id which can be rewritten as let
613 LetIds = [ID1|_],
614 !,
615 (atomic(ID1) -> add_internal_error(cleanup_pre,unwrapped_let_identifier(ID1)), INew=I
616 ; get_texpr_ids(LetIds,AtomicIDs),
617 remove_used_ids_from_info(AtomicIDs,I,INew)
618 ), % probably not necessary ?!
619 % see also the annotate_toplevel_exists rule above which adds allow_to_lift_exists; relevant, e.g., for test 1945
620 (member(allow_to_lift_exists,I) -> AddInfos=[allow_to_lift_exists] ; AddInfos=[]),
621 create_exists_opt(RestIds,NewPreds,AddInfos,NewP,_Modified),
622 (debug_mode(off) -> true
623 ; format('Extracted LET over ~w from exists (rest: ~w):~n ',[AtomicIDs,RestIds]),
624 translate:print_bexpr(b(let_predicate(LetIds,Exprs,NewP),pred,I)),nl
625 ).
626 % now the same LET extraction but for universal quantification:
627 cleanup_pre(forall(AllIds,P,Rhs),pred,I,let_predicate(LetIds,Exprs,NewP),pred,I,multi/forall_to_let) :-
628 get_preference(optimize_ast,true),
629 conjunction_to_nontyping_list(P,Preds),
630 % The ids are needed to build a "black list"
631 get_sorted_ids(AllIds,Ids),
632 check_forall_lhs_rhs(P,Rhs,I,Ids),
633 find_one_point_rules(AllIds,Preds,Ids,LetIds,Exprs,RestIds,NewPreds),
634 % only succeed if we found at least one id which can be rewritten as let
635 LetIds = [ID1|_],!,
636 (atomic(ID1) -> add_internal_error(cleanup_pre,unwrapped_let_identifier(ID1)) ; true),
637 conjunct_predicates_with_pos_info(NewPreds,NewLhs),
638 create_implication(NewLhs,Rhs,NewForallBody),
639 create_forall(RestIds,NewForallBody,NewP),
640 (debug_mode(on) -> print('Introduced let in forall: '), print(LetIds),nl ; true).
641 % warning: used_identifier information not yet computed; translate may generate warnings
642 cleanup_pre(exists(AllIds,P),pred,I0,NewPE,pred,NewI,multi/exists_remove_typing) :-
643 (is_a_conjunct(P,Typing,Q) ; is_an_implication(P,Typing,Q)),
644 % TRUE & Q == TRUE => Q == Q
645 is_typing_predicate(Typing),
646 % remove typing so that other exists rules can fire
647 % we run as cleanup_pre: the other simplifications which remove typing have not run yet
648 % such typing conjuncts typicially come from Rodin translations
649 create_exists_opt(AllIds,[Q],b(NewPE,_,I1)),
650 add_important_info_from_super_expression(I0,I1,I2), % we could also copy nodeid(_) from I0 ?
651 add_removed_typing_info(I2,NewI).
652 cleanup_pre(exists(AllIds,P),pred,I,disjunct(NewP1,NewP2),pred,I,single/partition_exists_implication) :-
653 is_a_disjunct_or_implication(P,_Type,Q,R),
654 /* note that even if R is only well-defined in case Q is false; it is ok to seperate this out
655 into two existential quantifiers: #x.(x=0 or 1/x=10) is ok to transform into #x.(x=0) or #x.(1/x=10) */
656 % this slows down test 1452, Cylinders, 'inv3/WD'; TO DO:investigate
657 create_exists_opt(AllIds,[Q],NewP1), % print('Q: '),print_bexpr(NewP1),nl,
658 create_exists_opt(AllIds,[R],NewP2). %, print('R: '),print_bexpr(NewP2),nl.
659 cleanup_pre(exists([B],P),pred,I,truth,pred,I,single/tautology_exists_min_max) :-
660 % ∃b·∀x0·(x0 ∈ FINITE ⇒ b ≤ x0) == TRUE : WD condition from Rodin for max; similar for min (TODO: check min)
661 B = b(identifier(ID1),integer,_),
662 get_texpr_expr(P,forall([X0],Left,Right)),
663 X0 = b(identifier(ID2),integer,_),
664 get_texpr_expr(Left,member(X1,FiniteSet)),
665 get_texpr_id(X1,ID2),
666 Right = b(COMP,pred,_),
667 (COMP = less_equal(B2,X2) ; COMP = greater_equal(B2,X2)),
668 get_texpr_id(B2,ID1), get_texpr_id(X2,ID2),
669 definitely_finite(FiniteSet),
670 (debug_mode(off) -> true ; format('Removing WD condition for min/max exists over ~w :',[ID1]), translate:print_bexpr(P),nl).
671 cleanup_pre(forall(AllIds,P,Rhs),pred,I,NewPred,pred,I,single/forall_to_post_let) :-
672 % translate something like !(x,y).(y:1..100 & x=y*y => x<=y) into
673 % !(y).(y : 1 .. 100 => (#(x).( (x)=(y * y) & x <= y)))
674 post_let_forall(AllIds,P,Rhs,NewPred,modification),
675 !,
676 (debug_mode(on) -> print('POST LET INTRODUCTION: '), print_bexpr(b(NewPred,pred,[])),nl ; true).
677 cleanup_pre(set_extension(List),Type,I, set_extension(NList),Type,I, single/remove_pos) :-
678 remove_position_info_from_list(List,I,NList),!.
679 cleanup_pre(sequence_extension(List),Type,I, sequence_extension(NList),Type,I, single/remove_pos) :-
680 remove_position_info_from_list(List,I,NList),!.
681 cleanup_pre(if(List),Type,I, if(NList),Type,I, single/remove_if_elsif_pos) :-
682 % the pos info is not used for individual if_elsif entries; some models contain very large if-then-else constructs
683 maplist(remove_top_levelposition_info,List,NList),!.
684 %cleanup_pre(concat(A,B),string,I,Res,string,I,multi/concat_assoc_reorder) :-
685 % A = b(concat(A1,A2),string,I1),
686 % !, % reorder STRING concats for better efficiency, can only occur when allow_sequence_operators_on_strings is true
687 % % TO DO: extract information I2B from A2 and B
688 % Res = concat(A1,b(concat(A2,B),string,I2B)).
689 cleanup_pre(typeset,SType,I,Expr,SType,I,multi/remove_typeset) :- !,
690 % used, e.g., in test 1205 for recursive Event-B operator definition
691 ( ground(SType) ->
692 (is_set_type(SType,Type),
693 create_maximal_type_set(Type,b(MaxExpr,_,_)) -> Expr=MaxExpr
694 ; is_set_type(SType,Type) ->
695 add_error_and_fail(b_ast_cleanup,'Creating type expression for typeset failed: ',Type)
696 ;
697 add_error_and_fail(b_ast_cleanup,'Creating type expression for typeset failed, type is not a set: ',SType)
698 )
699 ; add_error_and_fail(b_ast_cleanup,'Non-ground type for typeset expression: ',SType)).
700 cleanup_pre(integer_set(S),Type,I,Expr,Type,[was(integer_set(S))|I],multi/remove_integer_set) :- !,
701 translate_integer_set(S,I,Expr),
702 (debug_mode(off) -> true
703 ; format('Rewrite ~w to: ',[S]),
704 print_bexpr(b(Expr,integer,I)),nl).
705 % should we move the rewrite_rules to normalize ??
706 cleanup_pre(Expr,Type,Info,NewExpr,NewType,NewInfo, multi/apply_rewrite_rule(Rule)) :-
707 rewrite_rule_with_rename(Expr,Type,Info,NewExpr,NewType,NewInfo,Rule), % from b_ast_cleanup_rewrite_rules
708 (debug_mode(off) -> true
709 ; format('Use rewrite_rule ~w~n',[Rule]),
710 print_bexpr(b(NewExpr,NewType,NewInfo)),nl),
711 (ground(NewExpr) -> true ; print(not_ground_rewrite(NewExpr)),nl,fail).
712 % 'x > y' to 'y < x'
713 cleanup_pre(greater(Lhs,Rhs), pred, I, less(Rhs,Lhs), pred, [was(greater(Lhs,Rhs))|I], single/normalize_greater) :-
714 preferences:get_preference(normalize_ast, true).
715 % 'x >= y' to 'y <= x'
716 cleanup_pre(greater_equal(Lhs,Rhs), pred, I, less_equal(Rhs,Lhs), pred, [was(greater_equal(Lhs,Rhs))|I], single/normalize_greater_equal) :-
717 preferences:get_preference(normalize_ast, true).
718 % 'x - y' to 'x + -y'
719 cleanup_pre(minus(Lhs,Rhs), integer, I, add(Lhs,b(unary_minus(Rhs),integer,[])), integer, [was(minus(Lhs,Rhs))|I], single/normalize_minus) :-
720 preferences:get_preference(normalize_ast, true).
721 cleanup_pre(value(CLOSURE), Type, I, comprehension_set(TIDs,B), Type, I, single/normalize_value_closure) :-
722 nonvar(CLOSURE), CLOSURE=closure(P,T,B),
723 preferences:get_preference(normalize_ast, true),
724 create_typed_ids(P,T,TIDs).
725 cleanup_pre(comprehension_set(TIDs,Body), Type, I, union(Set1,Set2), Type,I, single/extract_union) :-
726 preferences:get_preference(normalize_ast, true),
727 is_a_disjunct(Body,B1,B2), % should we also detect set difference, should we detect common prefix typing
728 % {x| P or Q} ==> {x|P} \/ {x|Q}
729 % such closure values are created by symbolic union, relevant for JSON trace replay for test 281
730 safe_create_texpr(comprehension_set(TIDs,B1),Type,I,Set1),
731 safe_create_texpr(comprehension_set(TIDs,B2),Type,I,Set2).
732 cleanup_pre(external_function_call('ASSERT_EXPR',[BOOL,MSG,EXPR]), Type, I,
733 assertion_expression(Pred,MsgStr,EXPR), Type,I, single/detect_assertion_expression) :-
734 % translate ASSERT_EXPR back to assertion_expression; dual to way it is printed in pretty printer
735 get_pred_from_bool(BOOL,Pred),
736 get_string(MSG,MsgStr).
737 cleanup_pre(external_pred_call(PRED,ARGS), pred, I,
738 EQ, pred,I, single/rewrite_external_pred_to_bool_function) :-
739 preferences:get_preference(normalize_ast,true),
740 synonym_for_external_predicate(PRED,FUNC),
741 % replace PRED(ARGS) by bool(FUNC(ARGS)=TRUE) as external functions can always be used wo DEFINITIONS
742 safe_create_texpr(external_function_call(FUNC,ARGS),boolean,[],FUNCALL),
743 EQ = equal(FUNCALL,b(boolean_true,boolean,[])),
744 (debug_mode(off) -> true ; print('REWRITTEN external predicate call to '), print_bexpr(FUNCALL),nl).
745
746
747 % Cleanup PRE for function calls:
748 % In reply to PROB-240: Check if arguments of Prj1/2 are types only using is_just_type:
749 cleanup_pre_function(TProjection,Argument,_Type,I,Result,I,multi/projection_call) :-
750 get_texpr_expr(TProjection,Projection),
751 cleanup_function_projection(Projection,Argument,I,Result),
752 !.
753 cleanup_pre_function(Lambda,Argument,Type,I,NewContextExpr,I,multi/lambda_guard1) :-
754 get_preference(optimize_ast,true),
755 get_texpr_expr(Lambda,LambdaExpr),
756 is_lambda_in_context(LambdaExpr,Type,TIds,TPre,TVal,NewContextExpr,NewExpr,LocalIds),
757 (is_just_typing_pred(TPre)
758 -> TPre1 = b(truth,pred,[]), % relevant for replace count below
759 get_texpr_expr(TVal,AssertionExpr)
760 ; TPre1=TPre,
761 AssertionExpr = assertion_expression(TPre1,ErrMsg,TVal)
762 ),
763 get_texpr_ids(TIds,Ids),
764 nested_couple_to_list(Argument,ArgList),
765 % translate %x.(TPre|TVal)(arg) -> LET x BE x=arg IN ASSERT_EXPR(TPre,Msg,TVal) END
766 same_length(ArgList,TIds),
767 \+ some_id_occurs_in_expr(LocalIds,Argument), % would trigger here LET x BE x=1+1 IN %y.(y:0..x|y+x+x) END (x) = res & x=0
768 ( same_ids_and_types(ArgList,TIds)
769 -> % lambda argument names and provided arguments are identical
770 NewExpr = AssertionExpr % no LET has to be introduced; relevant e.g. for rule_sgc335.mch
771 ; \+ ( sort(Ids,SIds),
772 some_id_occurs_in_expr(SIds,Argument)
773 %,format('Not inlining lambda, parameter id ~w occurs in : ',[Id]), print_bexpr(Argument),nl
774 %, add_message(ast_cleanup,'Not inlining parameter: ',Id,I)
775 ), % otherwise name clash and we would need a LET that can treat LET x BE x=x+1
776 TAssertionExpr = b(AssertionExpr,Type,I),
777 NewExpr = let_expression(TIds,ArgList,TAssertionExpr)
778 ; Ids = [Id1], ArgList = [Arg1] ->
779 %TODO: safely treat multiple args and things like (%(x,v).(x:INTEGER|x*v)(v|->v))=100
780 % In this case we need to substitute all args in one go
781 replace_id_by_expr_with_count(TPre1,Id1,Arg1,TPre2,Count1),
782 replace_id_by_expr_with_count(TVal,Id1,Arg1,TVal2,Count2),
783 Count is Count1+Count2,
784 is_replace_id_by_expr_ok(Arg1,Id1,Count,lambda_guard1),
785 NewExpr = assertion_expression(TPre2,ErrMsg,TVal2)
786 ),
787 % simplify_let will remove simple let expressions and vars used only once
788 % we used to call replace_ids_by_args in all cases, but this can duplicate arguments
789 % not replacing did lead to test 1284 taking very long, 191 seconds instead of 0.3 for 192 states
790 !,
791 ajoin_with_sep(Ids,',',IdsAtom),
792 get_texpr_info(Lambda,LambdaInfo), translate_span(LambdaInfo,LSpan),
793 (get_definition_context_from_span(LambdaInfo,LSpan2)
794 -> ajoin(['lambda function %(',IdsAtom,') ',LSpan,' (', LSpan2,
795 ') called outside of domain, condition false: '],ErrMsg)
796 ; ajoin(['lambda function %(',IdsAtom,') ',LSpan,
797 ' called outside of domain, condition false: '],ErrMsg)),
798 (debug_mode(off) -> true
799 ; add_message(ast_cleanup,'INLINED function application for: ',Ids,I),
800 translate:nested_print_bexpr(b(NewExpr,Type,I)),nl
801 ).
802 cleanup_pre_function(Fun,Arg,integer,I,ArithOp,NewI,multi/succ_pred_optimisation) :-
803 precompute_pred_succ_function_call(Fun,Arg,ArithOp),
804 delete(I,contains_wd_condition,NewI). % we no longer have a WD condition; succ/pred are totally defined
805 /* cleanup_pre(event_b_comprehension_set([ID],ID,Pred),T,I,comprehension_set([Result],NewPred),T,
806 [was(event_b_comprehension_set)|I],multi/ev_compset_single_id) :-
807 % Event_B_Comprehension with a single ID which is also the expression
808 % TO DO: expand for multiple IDs
809 !,
810 Result = ID, NewPred=Pred. */
811 % Detect if_then_else; also done in cleanup_post (in pre we may be able to detect IF-THEN-ELSE before CSE has inserted lazy_lets
812 cleanup_pre_function(IFT,DUMMYARG,_Type,Info,if_then_else(IFPRED,THEN,ELSE),Info,multi/function_if_then_else) :-
813 is_if_then_else(IFT,pre,DUMMYARG,IFPRED,THEN,ELSE),
814 (debug_mode(off) -> true
815 ; print('% Recognised if-then-else expression (pre): IF '), print_bexpr(IFPRED),
816 print(' THEN '),print_bexpr(THEN), print(' ELSE '), print_bexpr(ELSE),nl
817 ).
818
819 % check if we detect a lambda or a lambda wrapped inside let_expressions
820 % e.g., LET one BE one=1 IN LET two BE two=2 IN %(x).(x:INTEGER|x+one+one+two+two)END END(y) = 7
821 % NewType: by moving the function application into the LETs the type of the LETs need to be adapted
822 is_lambda_in_context(let_expression(LetIds,Exprs,b(Lambda,_OldType,Info)),NewType,TIds,TPred,TValue,
823 let_expression(LetIds,Exprs,b(InnerCtxt,NewType,Info)),Hole,NewLocalIds) :- !,
824 is_lambda_in_context(Lambda,NewType,TIds,TPred,TValue,InnerCtxt,Hole,LocalIds),
825 get_texpr_ids(LetIds,Ids),
826 sort(Ids,SIds),
827 ord_union(SIds,LocalIds,NewLocalIds).
828 is_lambda_in_context(Lambda,_NewType,TIds,TPred,TValue,Context,ReplacementHole,[]) :-
829 Context=ReplacementHole,
830 is_lambda(Lambda, TIds, TPred,TValue).
831
832 :- use_module(closures,[is_lambda_closure/7, is_lambda_comprehension_set/4]).
833 is_lambda(lambda(TIds,TPred,TValue), TIds, TPred,TValue) :- !.
834 is_lambda(event_b_comprehension_set([TId],Expr,TPred), [TId], TPred, TValue) :- !,
835 % rewrite_event_b_comprehension_set does not seem to get called before the function/lambda rule is applied
836 % {ID.ID|->Val | PRed}
837 Expr = b(couple(LHS,RHS),_,_),
838 same_texpr(LHS,TId),
839 TValue=RHS.
840 is_lambda(value(Closure),[TId],TPred,TValue) :- !, nonvar(Closure), Closure = closure(Args,Types,Body),
841 is_lambda_closure(Args,Types,Body, [OtherID], [OtherType], TPred, TValue),
842 create_typed_id(OtherID,OtherType,TId). % TODO: accept lambdas with more than one argument TId
843 is_lambda(CompSet,TIds,Pred,Val) :- CompSet=comprehension_set(_,_),
844 is_lambda_comprehension_set(b(CompSet,any,[]),TIds,Pred,Val).
845
846 is_just_typing_pred(b(E,_,_)) :- is_just_typing_pred2(E).
847 is_just_typing_pred2(truth).
848 is_just_typing_pred2(conjunct(A,B)) :- is_just_typing_pred(A), is_just_typing_pred(B).
849 is_just_typing_pred2(member(_,B)) :- is_just_type(B). % would be removed by remove_type_member rule
850
851
852 :- use_module(external_function_declarations,[synonym_for_external_predicate/2]).
853
854 get_string(b(string(S),_,_),S).
855 get_string(b(value(V),_,_),S) :- nonvar(V), V=string(Str), atom(Str), S=Str.
856 % translate a boolean value into a predicate:
857 get_pred_from_bool(b(convert_bool(P),_,_),Pred) :- !, Pred=P.
858 get_pred_from_bool(BOOL,b(equal(BOOL,BTRUE),pred,[])) :- BTRUE = b(boolean_true,boolean,[]).
859
860 translate_integer_set('NAT',I,interval(b(integer(0),integer,I),b(max_int,integer,I))).
861 translate_integer_set('NAT1',I,interval(b(integer(1),integer,I),b(max_int,integer,I))).
862 translate_integer_set('INT',I,interval(b(min_int,integer,I),b(max_int,integer,I))).
863 %translate_integer_set('INTEGER',I,comprehension_set([b(identifier('_zzzz_unary'),integer,I)],
864 % b(truth,pred,[prob_annotation('SYMBOLIC')|I]))).
865 %translate_integer_set('NATURAL',I,comprehension_set([b(identifier('_zzzz_unary'),integer,I)],
866 % b(greater_equal(b(identifier('_zzzz_unary'),integer,I),
867 % b(integer(0),integer,I)),pred,[prob_annotation('SYMBOLIC')|I]))).
868 %translate_integer_set('NATURAL1',I,comprehension_set([b(identifier('_zzzz_unary'),integer,I)],
869 % b(greater_equal(b(identifier('_zzzz_unary'),integer,I),
870 % b(integer(1),integer,I)),pred,[prob_annotation('SYMBOLIC')|I]))).
871
872 % detect if an expression is equivalent to an integer set, does not check for interval yet
873 is_integer_set(integer_set(S),S).
874 is_integer_set(comprehension_set([b(identifier(ID),integer,_)],b(B,_,_)),S) :-
875 ? is_integer_set_constraint_pred(B,ID,S).
876 is_integer_set_constraint_pred(truth,_,'INTEGER').
877 is_integer_set_constraint_pred(Expr,ID,Set) :-
878 is_greater_equal(Expr,b(identifier(ID),integer,_),TNr),
879 get_integer(TNr,Nr),
880 (Nr=0 -> Set='NATURAL' ; Nr=1 -> Set='NATURAL1').
881 is_integer_set_constraint_pred(Expr,ID,Set) :-
882 is_greater(Expr,b(identifier(ID),integer,_),TNr),
883 get_integer(TNr,Nr),
884 (Nr = -1 -> Set='NATURAL' ; Nr=0 -> Set='NATURAL1').
885
886 is_greater_equal(greater_equal(A,B),A,B).
887 is_greater_equal(less_equal(B,A),A,B).
888 is_greater(greater(A,B),A,B).
889 is_greater(less(B,A),A,B).
890
891 is_inf_integer_set_with_lower_bound(b(X,_,_),Bound) :- is_integer_set(X,N),
892 (N='NATURAL' -> Bound=0 ; N='NATURAL1' -> Bound=1).
893
894
895
896 is_subset(subset(A,B),A,B).
897 is_subset(member(A,b(pow_subset(B),_,_)),A,B). % x : POW(T) <=> x <: T
898
899 % tool to translate CASE values to Test predicates for IF-THEN-ELSE
900 gen_if_elsif(CaseID,b(case_or(ListOfValues, Body),_,I),
901 b(if_elsif(Test,Body),subst,I)) :-
902 get_texpr_type(CaseID,T),
903 SEXT = b(set_extension(ListOfValues),set(T),I),
904 Test = b(member(CaseID,SEXT),pred,I).
905
906 % the case below happens frequently in data validation:
907 remove_position_info_from_list(List,I,NList) :-
908 member(nodeid(pos(C,FilePos,Line,From,Line,To)),I),
909 To-From > 1000, % the entire set/sequence extension is on one large line
910 length(List,Len), Len>100, % it has many elements
911 % we replace all position infos by the same top-level position info (enabling sharing)
912 maplist(remove_position_info(nodeid(pos(C,FilePos,Line,From,Line,To))),List,NList),
913 (debug_mode(off) -> true
914 ; format('SIMPLIFY POSITION INFO IN SET/SEQUENCE EXTENSION: line # ~w, length ~w~n',[Line,Len])).
915 remove_position_info(NI,b(Expr,Type,Infos),b(NExpr,Type,NewInfos)) :-
916 syntaxtransformation(Expr,Subs,_Names,NSubs,NExpr),
917 (select(nodeid(pos(_,_FilePos,_,_,_,_)),Infos,NT) -> NewInfos=[NI|NT] ; NewInfos=Infos),
918 maplist(remove_position_info(NI),Subs,NSubs).
919
920 :- use_module(bsyntaxtree,[delete_pos_info/2]).
921 % just remove position infos from top-lefel
922 remove_top_levelposition_info(b(Expr,Type,Infos),b(Expr,Type,NewInfos)) :-
923 delete_pos_info(Infos,NewInfos). % TODO: maybe remove other useless infos
924
925 % rules for function application of various projection functions
926 cleanup_function_projection(first_projection(A,B),Argument,I,Result) :-
927 gen_assertion_expression(A,B,Argument,first_of_pair(Argument),first,I,Result).
928 cleanup_function_projection(second_projection(A,B),Argument,I,Result) :-
929 gen_assertion_expression(A,B,Argument,second_of_pair(Argument),second,I,Result).
930 cleanup_function_projection(event_b_second_projection(A),Argument,_I,Result) :- % old style Rodin projection
931 check_is_just_type(A),Result = first_of_pair(Argument).
932 cleanup_function_projection(event_b_second_projection(A),Argument,_I,Result) :- % old style Rodin projection
933 check_is_just_type(A),Result = second_of_pair(Argument).
934 cleanup_function_projection(event_b_first_projection_v2,Argument,_I,Result) :- Result = first_of_pair(Argument).
935 cleanup_function_projection(event_b_second_projection_v2,Argument,_I,Result) :- Result = second_of_pair(Argument).
936
937 check_is_just_type(_A) :- preferences:get_preference(ignore_prj_types,true),!.
938 check_is_just_type(A) :- (is_just_type(A) -> true ; debug_println(9,not_type_for_prj(A)),fail).
939
940 :- use_module(bsyntaxtree,[get_texpr_set_type/2, create_cartesian_product/3]).
941 gen_assertion_expression(A,B,_Argument,ProjExpr,_ProjType,_I,Result) :-
942 check_is_just_type(A),check_is_just_type(B),
943 !,
944 Result = ProjExpr.
945 % TO DO: add simplification rule for couple(x,y) : A*B with A or B being just types
946 gen_assertion_expression(A,B,Argument,ProjExpr,ProjType,Info,Result) :-
947 create_cartesian_product(A,B,CartAB),
948 safe_create_texpr(member(Argument,CartAB),pred,MemCheck),
949 ErrMsg = 'projection function called outside of domain: ', % TO DO: provide better user message with Argument result
950 perfmessage('Projection function has WD condition which needs to be checked at runtime (use prj1/prj2 without arguments or set IGNORE_PRJ_TYPES preference to TRUE)',Info),
951 (ProjType == first -> get_texpr_set_type(A,TT) ; get_texpr_set_type(B,TT)), %%
952 %get_texpr_pos_infos(Argument,Info), % add position infos
953 extract_pos_infos(Info,PosInfo),
954 safe_create_texpr(ProjExpr,TT,PosInfo,TProjExpr),
955 Result = assertion_expression(MemCheck,ErrMsg,TProjExpr).
956
957
958
959 % rewriting Event-B comprehension sets into classical B style ones
960 rewrite_event_b_comprehension_set(IDs,CoupleExpr,Pred, _T, NewExpression) :-
961 % detect lambda expressions in classical B style
962 nested_couple_to_list(CoupleExpr,List),
963 check_ids(IDs,List,Expr),!,
964 NewExpression = lambda(IDs,Pred,Expr).
965 rewrite_event_b_comprehension_set(IDList,CoupleExpr,Pred, _T, NewExpression) :-
966 % Event_B_Comprehension with a several IDs which are also used as the couple expression
967 nested_couple_to_list(CoupleExpr,List),
968 List = IDList,
969 !,
970 NewExpression = comprehension_set(IDList,Pred).
971 rewrite_event_b_comprehension_set(Ids,Expr,Pred, T, NewExpression) :-
972 NewExpression = comprehension_set([Result],NewPred),
973 unify_types_strict(T,set(Type)),
974 % print(event_b_comprehension_set(Ids,Expr,Pred)),nl,
975 (select(Expr,Ids,RestIds)
976 -> % the Expr is an identifier which is part of Ids: we can avoid complicated translation below
977 % example {f,n•n:INT & f:1..n-->Digit|f} --translated-> {f|#(n).(n:INT & f:1..n-->Digit)}
978 % print(remove(Expr,RestIds)),nl,
979 ExPred=Pred, Result=Expr
980 ; get_unique_id_inside('__comp_result__',Pred,Expr,ResultId),
981 create_texpr(identifier(ResultId),Type,[],Result),
982 safe_create_texpr(equal(Result,Expr),pred,Equal),
983 conjunct_predicates_with_pos_info(Pred,Equal,ExPred), % put Equal after Pred for WD; used to be other order!
984 RestIds=Ids
985 ),
986 create_exists(RestIds,ExPred,NewPred). %, print(done_rewrite_event_b_comprehension_set),nl, print_bexpr(NewPred),nl.
987
988 check_ids([],[CoupleExpr],CoupleExpr). % we terminate with a single expression
989 check_ids([ID|T],[CoupleExprID|CT],Rest) :-
990 same_id(ID,CoupleExprID,_),
991 check_ids(T,CT,Rest).
992
993
994 evaluate_seq_extension_to_avl(List,AVL,LocalIds) :-
995 evaluate_set_extension(List,EvaluatedList,LocalIds),
996 convert_set_to_seq(EvaluatedList,1,ESeq),
997 convert_to_avl(ESeq,AVL).
998
999 evaluate_set_extension([],[],_).
1000 evaluate_set_extension([H|List],[EH|EvaluatedList],LocalIds) :-
1001 eval_set_extension_element(H,EH,LocalIds),
1002 evaluate_set_extension(List,EvaluatedList,LocalIds).
1003
1004 extension_should_be_evaluated(List) :-
1005 %preferences:has_default_value(use_solver_on_load), % Kodkod could not translate booleans back; now it can; check if we still need this
1006 preferences:get_preference(optimize_ast,true),
1007 List \= [],
1008 List = [_|ListT], ListT \= []. % do not do this for singleton sets so as not to prevent triggering of other rules
1009
1010 :- use_module(kernel_reals,[construct_real/2, construct_negative_real/2]).
1011 % construct a value term for a simple AST element:
1012 eval_set_extension_element(b(E,T,_I),EE,LocalIds) :-
1013 (eval_set_extension_aux(E,EE,LocalIds) -> true
1014 ; eval_set_extension_typed_aux(E,T,EE,LocalIds)
1015 % ; print('eval_set_extension failed: '),tools_printing:print_term_summary(E),nl,translate:print_span(_I),nl,fail
1016 ).
1017 eval_set_extension_aux(boolean_false,pred_false,_).
1018 eval_set_extension_aux(boolean_true,pred_true,_).
1019 eval_set_extension_aux(couple(A,B),(EA,EB),LocalIds) :-
1020 eval_set_extension_element(A,EA,LocalIds), eval_set_extension_element(B,EB,LocalIds).
1021 eval_set_extension_aux(integer(I),int(I),_).
1022 eval_set_extension_aux(real(Atom),Real,_) :- construct_real(Atom,Real).
1023 eval_set_extension_aux(unary_minus(b(Val,_,_)),Res,_) :- eval_set_ext_minus(Val,Res).
1024 eval_set_extension_aux(string(S),string(S),_).
1025 eval_set_extension_aux(empty_set,[],_).
1026 eval_set_extension_aux(empty_sequence,[],_).
1027 eval_set_extension_aux(value(V),V,_).
1028 eval_set_extension_aux(rec(Fields),rec(EF),LocalIds) :- eval_set_extension_fields(Fields,EF,LocalIds).
1029 %eval_set_extension_aux(interval(A,B),...) :- ...
1030 eval_set_extension_aux(set_extension(List),AVL,LocalIds) :-
1031 evaluate_set_extension(List,EvaluatedList,LocalIds),
1032 convert_to_avl(EvaluatedList,AVL).
1033 eval_set_extension_aux(sequence_extension(List),AVL,LocalIds) :-
1034 evaluate_seq_extension_to_avl(List,AVL,LocalIds).
1035
1036
1037 :- use_module(probsrc(kernel_freetypes),[registered_freetype_case_value/3, freetype_case_db/3]).
1038 eval_set_extension_typed_aux(identifier(ID),TYPE,FDVal,LocalIds) :- LocalIds \= not_available,
1039 (TYPE = global(GType)
1040 -> b_global_sets:lookup_global_constant(ID,FDVal),
1041 ord_nonmember(ID,LocalIds),
1042 % we could check if enumerated_set_element entry in infos, cf is_just_type3 with given_set info
1043 % may not yet be fully precompiled, hence we rely on pre_register_enumerated_set_with_elems
1044 FDVal = fd(_,GType) % just check that the type matches, in case ID is a local name with different type
1045 ; TYPE = freetype(FType)
1046 -> % see b_global_set_or_free_type; we could also accept global sets and freetype sets
1047 registered_freetype_case_value(ID,TYPE,FDVal),
1048 ord_nonmember(ID,LocalIds),
1049 FDVal = freeval(FType,_,_) % check type matches
1050 ).
1051 eval_set_extension_typed_aux(function(TID,Args),freetype(FreetypeId),V,LocalIds) :- LocalIds \= not_available,
1052 ground(FreetypeId),
1053 get_texpr_id(TID,CaseId),
1054 freetype_case_db(CaseId,FreetypeId,_CaseType),
1055 ord_nonmember(CaseId,LocalIds),
1056 %write(case(CaseId,FreetypeId,CaseType)),nl,
1057 eval_set_extension_element(Args,EArgs,LocalIds),
1058 V = freeval(FreetypeId,CaseId,EArgs).
1059 %eval_set_extension_typed_aux(E,T,_,_) :- print(uncov(E,T)),nl,nl,fail.
1060
1061
1062 eval_set_ext_minus(integer(I),int(R)) :- R is -I.
1063 eval_set_ext_minus(real(Atom),Real) :- construct_negative_real(Atom,Real).
1064
1065 eval_set_extension_fields([],[],_LocalIds).
1066 eval_set_extension_fields([field(Name,V)|T],[field(Name,EV)|ET],LocalIds) :- eval_set_extension_element(V,EV,LocalIds),
1067 eval_set_extension_fields(T,ET,LocalIds).
1068
1069 convert_set_to_seq([],_,[]).
1070 convert_set_to_seq([H|T],N,[(int(N),H)|CT]) :- N1 is N+1, convert_set_to_seq(T,N1,CT).
1071
1072
1073 post_let_forall(AllIds,P,Rhs,NewPred,modification) :-
1074 conjunction_to_list(P,Preds), reverse(Preds,RPreds),
1075 ? select_equality(TId,RPreds,[],_,Expr,RRest,UsedIds,no_check),
1076 ? select(TId,AllIds,RestIds),
1077 get_texpr_id(TId,Id),
1078 \+ member(Id,UsedIds), % not a recursive equality
1079 reverse(RRest,Rest),
1080 conjunct_predicates_with_pos_info(Rest,RestPred),
1081 \+ occurs_in_expr(Id,RestPred),
1082 !,
1083 NewRhs = b(let_predicate([TId],[Expr],Rhs),pred,[]),
1084 post_let_forall(RestIds,RestPred,NewRhs,NewPred,_).
1085 post_let_forall(AllIds,P,Rhs,NewPred, no_modification) :-
1086 create_implication(P,Rhs,NewForallBody),
1087 create_forall(AllIds,NewForallBody,NewP),
1088 get_texpr_expr(NewP,NewPred).
1089
1090
1091 is_interval(b(interval(A,B),_,_),A,B).
1092 is_interval(b(value(V),set(_),_),A,B) :- nonvar(V), V=closure(P,T,B),
1093 custom_explicit_sets:is_interval_closure(P,T,B,LOW,UP), integer(LOW),integer(UP),
1094 A=b(integer(LOW),integer,[]),
1095 B=b(integer(UP),integer,[]).
1096
1097 % a more flexible version, also detecting singleton set extension
1098 ?is_interval_or_singleton(I,A,B) :- is_interval(I,A,B),!.
1099 is_interval_or_singleton(b(set_extension([A]),set(integer),_),A,A).
1100
1101
1102
1103 % create a lambda expression for a projection
1104 create_projection_set(A,B,_Switch,Res) :-
1105 (definitely_empty_set(A) ; definitely_empty_set(B)),!,
1106 Res = empty_set.
1107 create_projection_set(A,B,Switch,lambda(Ids,SPred,Expr)) :- % generate lambda to be able to use function(lambda) rule
1108 Ids = [TArg1,TArg2],
1109 ( Switch==first -> Expr = TArg1
1110 ; Switch==second -> Expr = TArg2),
1111 get_texpr_type(A,TA1), unify_types_strict(TA1,set(Type1)),
1112 get_texpr_type(B,TB2), unify_types_strict(TB2,set(Type2)),
1113 ? (contains_no_ids(A,B) -> Arg1 = '_zzzz_unary', Arg2 = '_zzzz_binary' % avoid generating fresh ids; relevant for test 1313 and ticket PROB-346
1114 % TO DO: check whether _zzzz_unary/binary are actually used; we should avoid generating fresh ids whenever possible (otherwise syntactically identical formulas become different)
1115 ; get_unique_id_inside('_prj_arg1__',A,B,Arg1),
1116 get_unique_id_inside('_prj_arg2__',A,B,Arg2)),
1117 create_texpr(identifier(Arg1),Type1,[generated(Switch)],TArg1),
1118 create_texpr(identifier(Arg2),Type2,[generated(Switch)],TArg2),
1119 safe_create_texpr(member(TArg1,A),pred,MembA),
1120 safe_create_texpr(member(TArg2,B),pred,MembB),
1121 conjunct_predicates([MembA,MembB],Pred), SPred=Pred.
1122 % bsyntaxtree:mark_bexpr_as_symbolic(Pred,SPred). % TO DO: put mark code into another module; maybe only mark as symbolic if types large enough ??
1123
1124 ?contains_no_ids(A,B) :- contains_no_ids(A), contains_no_ids(B).
1125 ?contains_no_ids(b(E,_,_)) :- contains_no_ids_aux(E).
1126 contains_no_ids_aux(bool_set).
1127 contains_no_ids_aux(X) :- is_integer_set(X,_). % comprehension set may contain ids, but not visible to outside
1128 contains_no_ids_aux(mult_or_cart(A,B)) :- contains_no_ids(A),contains_no_ids(B).
1129 contains_no_ids_aux(relations(A,B)) :- contains_no_ids(A),contains_no_ids(B).
1130 contains_no_ids_aux(pow_subset(A)) :- contains_no_ids(A).
1131 contains_no_ids_aux(pow1_subset(A)) :- contains_no_ids(A).
1132 contains_no_ids_aux(real_set).
1133 contains_no_ids_aux(string_set).
1134 contains_no_ids_aux(interval(A,B)) :- contains_no_ids(A),contains_no_ids(B).
1135 % TO DO: add more
1136
1137 create_event_b_projection_set(Rel,Switch,lambda(Ids,SPred,Expr)) :-
1138 Ids = [TArg1,TArg2],
1139 ( Switch==first -> Expr = TArg1
1140 ; Switch==second -> Expr = TArg2),
1141 get_texpr_type(Rel,RT),unify_types_strict(RT,set(couple(Type1,Type2))),
1142 get_unique_id_inside('_prj_arg1__',Rel,Arg1),
1143 get_unique_id_inside('_prj_arg2__',Rel,Arg2),
1144 create_texpr(identifier(Arg1),Type1,[generated(Switch)],TArg1),
1145 create_texpr(identifier(Arg2),Type2,[generated(Switch)],TArg2),
1146 create_texpr(couple(TArg1,TArg2),couple(Type1,Type2),[],Couple),
1147 safe_create_texpr(member(Couple,Rel),pred,Member),
1148 SPred=Member.
1149 %bsyntaxtree:mark_bexpr_as_symbolic(Pred,SPred).
1150
1151 create_event_b_projection_set_v2(RelType,Switch,comprehension_set(Ids,SPred)) :-
1152 % we are generating {p1,p2,lambda | lambda=p1/p2}
1153 Ids = [TArg1,TArg2,TArg3],
1154 ( Switch==first -> ResultExpr = TArg1, Type1 = Type3
1155 ; Switch==second -> ResultExpr = TArg2, Type2 = Type3),
1156 unify_types_strict(RelType,set(couple(couple(Type1,Type2),T3))),
1157 (T3==Type3 -> true ; add_error(create_event_b_projection_set,'Unexpected return type: ',T3)),
1158 Arg1 = '_zzzz_unary',
1159 Arg2 = '_zzzz_binary',
1160 Arg3 = '_lambda_result_', % the comprehension set contains no other expressions: no clash possible
1161 create_texpr(identifier(Arg1),Type1,[generated(Switch)],TArg1),
1162 create_texpr(identifier(Arg2),Type2,[generated(Switch)],TArg2),
1163 create_texpr(identifier(Arg3),Type3,[lambda_result(Arg3),generated(Switch)],TArg3),
1164 safe_create_texpr(equal(TArg3,ResultExpr),pred,[prob_annotation('LAMBDA-EQUALITY')],Equal),
1165 SPred=Equal.
1166 % bsyntaxtree:mark_bexpr_as_symbolic(Pred,SPred).
1167 %,print(Pred),nl.
1168
1169 :- use_module(btypechecker,[couplise_list/2,prime_identifiers/2,prime_identifiers0/2, prime_atom0/2]).
1170 % create a comprehension set for quantified union or intersection UNION(x).(P|E) = ran(%x.(P|E))
1171 % TO DO: translate UNION into UNION(x).(P|E) = dom({r,x|P & r:E}) = ran({x,r|P & r:E}) which is considerably faster
1172 % also works for e.g., UNION(x).(x:1..2|{x+y}) = 12..13
1173 %quantified_union_op(Ids,Pred,Expr,SetType,Res) :- is_set_type(SetType,Type),
1174 % !,
1175 % Info = [generated(quantified_union)],
1176 % get_unique_id_inside('_zzzz_unary',Pred,Expr,FRESHID), % also include Expr !
1177 % NewID = b(identifier(FRESHID),Type,[]), %fresh
1178 % append(Ids,[NewID],NewIds),
1179 % safe_create_texpr(member(NewID,Expr),pred,[],Member),
1180 % conjunct_predicates([Pred,Member],Body),
1181 % get_texpr_types(NewIds,Types),couplise_list(Types,TupleType),
1182 % safe_create_texpr(comprehension_set(NewIds,Body),set(TupleType),Info,ComprSet),
1183 % Res = range(ComprSet).
1184 % %safe_create_texpr(range(ComprSet),set(set(Type)),Info,Set), print_bexpr(Set),nl.
1185 %quantified_union_op(Ids,Pred,Expr,SetType,Set) :-
1186 % add_internal_error('Could not translate quantified UNION operator: ',quantified_union_op(Ids,Pred,Expr,SetType,Set)),
1187 % fail.
1188
1189 % create a comprehension set for quantified union or intersection INTER(x).(P|E) = inter(ran(%x.(P|E)))
1190 % UNION could be treated by quantified_union_op above
1191 quantified_set_op(Ids,Pred,Expr,Loc,OuterInfos,Set) :-
1192 create_range_lambda(Ids,Pred,Expr,Loc,OuterInfos,Set),
1193 !. % , print(quantified),nl,print_bexpr(Set),nl.
1194 quantified_set_op(Ids,Pred,Expr,Loc,OuterInfos,Set) :-
1195 add_internal_error('Could not translate quantified set operator: ',
1196 quantified_set_op(Ids,Pred,Expr,Loc,OuterInfos,Set)),
1197 fail.
1198
1199 create_range_lambda(Ids,Pred,Expr,Loc,OuterInfos,Set) :-
1200 Info = [generated(Loc)|OuterInfos],
1201 get_texpr_types(Ids,Types),couplise_list(Types,ArgType),
1202 get_texpr_type(Expr,ExprType),
1203 safe_create_texpr(lambda(Ids,Pred,Expr),set(couple(ArgType,ExprType)),Info,Lambda),
1204 safe_create_texpr(range(Lambda),set(ExprType),Info,Set).
1205
1206
1207 quantified_set_operator(quantified_union(AllIds,Pred,Expr),quantified_union,AllIds,Pred,Expr).
1208 quantified_set_operator(quantified_intersection(AllIds,Pred,Expr),quantified_intersection,AllIds,Pred,Expr).
1209
1210 % construct UNION(QIds).(QPreds|Expr)
1211 construct_quantified_set_operator(QuantOP,QIds,QPreds,Expr,Type,Info, NewExpr) :-
1212 (QIds=[],QPreds=[] -> NewExpr=Expr
1213 % UNION(x).(x=E|Expr) --> LET x BE x=E in Expr END
1214 ; QIds = [] -> conjunct_predicates_with_pos_info(QPreds,NewPred),
1215 % UNION(x).(x=E & NewPred|Expr) --> LET x BE x=E in IF NewPRED THEN Expr ELSE {} END END
1216 NewExpr = b(if_then_else(NewPred,Expr,b(empty_set,Type,[])),Type,Info)
1217 ; conjunct_predicates_with_pos_info(QPreds,NewPred),
1218 quantified_set_operator(NewQUANT,QuantOP,QIds,NewPred,Expr),
1219 NewExpr = b(NewQUANT,Type,Info)
1220 ).
1221
1222 % match_ids(List1,List2, AllIds,Rest) find all ids in List1 in List2; ids in List2 not in List1 are put in Rest
1223 match_ids([],List,List,List).
1224 ?match_ids([TID|T],List,[TID2|T2],Rest) :- select(TID2,List,L2),
1225 same_id(TID,TID2,_),!,
1226 match_ids(T,L2,T2,Rest).
1227
1228 % generate dom(.) operators for TypedIds which have to be projected away
1229 generate_dom_for_ids([],E,T,I,b(E,T,I)).
1230 generate_dom_for_ids([TID|Ts],E,T,I,Res) :- T = set(T1),
1231 get_texpr_type(TID,TIDType),
1232 generate_dom_for_ids(Ts,E,set(couple(T1,TIDType)),I,DE),
1233 safe_create_texpr(domain(DE),T,Res).
1234
1235 :- use_module(tools_strings,[ajoin/2,ajoin_with_sep/3]).
1236 % we ensure that this check is only done once, for user machines,... not for generated formulas
1237 check_forall_lhs_rhs(_,_,_,_) :- preferences:get_preference(perform_stricter_static_checks,false),!.
1238 check_forall_lhs_rhs(_,_,_,_) :- preferences:get_preference(disprover_mode,true),!.
1239 check_forall_lhs_rhs(_,_,_,_) :- animation_minor_mode(eventb),!. % typing predicates get removed it seems
1240 ?check_forall_lhs_rhs(_,_,I,_) :- member(removed_typing,I),!. % means that typing was possibly removed
1241 ?check_forall_lhs_rhs(Lhs,_,_,_) :- member_in_conjunction(PC,Lhs),
1242 ? get_texpr_info(PC,PI),member(II,PI),
1243 removed_typing(II),!. % it was something else; does not seem to detect all removed conjunctions, hence we also check I above
1244 check_forall_lhs_rhs(P,_,I,Ids) :- find_identifier_uses_if_necessary(P,[],LhsUsed),
1245 ord_subtract(Ids,LhsUsed,NotDefined),
1246 NotDefined=[_|_],
1247 ajoin_with_sep(NotDefined,',',S),
1248 translate:translate_bexpression(P,PS),
1249 ajoin(['Left-hand side "', PS, '" of forall does not define identifier(s): '],Msg),
1250 add_warning(bmachine_static_checks,Msg,S,I),
1251 fail.
1252 check_forall_lhs_rhs(_,Rhs,I,Ids) :-
1253 find_identifier_uses_if_necessary(Rhs,[],RhsUsed),
1254 (ord_intersect(Ids,RhsUsed) -> true
1255 ; ajoin_with_sep(Ids,',',S),
1256 add_warning(bmachine_static_checks,'Right-hand side of forall does not use identifiers: ',S,I)
1257 ).
1258 removed_typing(removed_typing). removed_typing(was(_)).
1259
1260 :- use_module(kernel_records,[normalise_record_type/2]).
1261 :- use_module(library(lists),[last/2]).
1262
1263 % first the rules that require the path:
1264 cleanup_post_with_path(assign([b(identifier(ID),TYPE,INFO)],[EXPR]),subst,I,
1265 assign_single_id(b(identifier(ID),TYPE,INFO),EXPR),subst,I,single/assign_single_id,Path) :-
1266 \+ animation_minor_mode(eventb), % there is no support in the Event-B interpreter for assign_single_id yet
1267 ? (simple_expression(EXPR) % the assign_single_id is not guarded by a waitflag; EXPR should not be too expensive too calculate
1268 -> true
1269 ; % if we are in an unguarded context; then we do not need to guard EXPR by waitflag anyway
1270 ? maplist(unguarded,Path)
1271 ),
1272 !,
1273 (debug_level_active_for(4) -> format('Single Assignment to ~w~n',[ID])
1274 %translate:print_subst(b(assign([b(identifier(ID),TYPE,INFO)],[EXPR]),subst,I)),nl
1275 ; true).
1276 cleanup_post_with_path(any(Ids,Pred,Subst),subst,Info,any(Ids,Pred,NewSubst),subst,NewInfo,multi/remove_useless_assign,_Path) :-
1277 get_preference(optimize_ast,true),
1278 member_in_conjunction(b(equal(TID1,TID2),pred,_),Pred),
1279 get_texpr_id(TID1,ID1),
1280 get_texpr_id(TID2,ID2), % we have an equality of the form x=x' (as generated by TLA2B)
1281 delete_assignment(Subst,TID3,TID4,NewSubst),
1282 get_texpr_id(TID4,ID4), get_texpr_id(TID3,ID3),
1283 ( c(ID1,ID2) = c(ID3,ID4) ; c(ID1,ID2) = c(ID4,ID3)), % we have an assignment x:=x' or x':=x
1284 % the cleanup rule recompute_accessed_vars below recomputes the info fields for enclosing operations! get_accessed_vars is currently called before ast_cleanup
1285 ajoin([ID3,' := ',ID4],Assign),
1286 add_hint_message(remove_useless_assign,'Removing useless assignment: ',Assign,Info),
1287 (member(removed_useless_assign,Info) -> NewInfo=Info ; NewInfo=[removed_useless_assign|Info]).
1288 cleanup_post_with_path(operation(TName,Res,Params,TBody),Type,Info,
1289 operation(TName,Res,Params,NewTBody),Type,NewInfos,single/recompute_accessed_vars,_Path) :-
1290 TBody=b(Body,subst,BInfos),
1291 select(removed_useless_assign,BInfos,NewBInfos),
1292 btypechecker:compute_accessed_vars_infos_for_operation(TName,Res,Params,TBody,Modifies,_,_,_,NewRWInfos),
1293 debug_format(19,'Recomputing read/write infos for ~w (~w)~n',[TName,Modifies]),
1294 update_infos(NewRWInfos,Info,NewInfos),
1295 NewTBody=b(Body,subst,NewBInfos).
1296 cleanup_post_with_path(any(Ids,Pred,Subst),subst,I,Result,subst,[generated|I],single/transform_any_into_let,Path) :-
1297 (last(Path,path_arg(top_level(_),_))
1298 /* do not remove top-level ANY if show_eventb_any_arguments is true; see, e.g., test 1271 */
1299 -> preferences:preference(show_eventb_any_arguments,false) ; true),
1300 conjunction_to_list(Pred,Preds),
1301 get_sorted_ids(Ids,BlacklistIds),
1302 find_one_point_rules(Ids,Preds,BlacklistIds,LetIDs,Exprs,RestIds,RestPreds),
1303 LetIDs \= [],
1304 maplist(create_equality,LetIDs,Exprs,LetDefs),
1305 % print(found_lets(LetIDs,RestIds,RestPred)),nl,print(Path),nl,
1306 conjunct_predicates_with_pos_info(LetDefs,LetDefPred),
1307 (RestIds = [], RestPreds=[] % complete ANY can be translated to LET
1308 -> Result = let(LetIDs,LetDefPred,Body), Body = Subst
1309 ? ; split_predicates(RestPreds,Ids,RestUsingIds,RestNotUsingIds),
1310 conjunct_predicates_with_pos_info(RestPreds,RestPred),
1311 % print('USING: '), print_bexpr(RestUsingIds),nl, print('NOT USING: '), print_bexpr(RestNotUsingIds),nl,
1312 (RestIds = []
1313 -> (is_truth(RestUsingIds)
1314 % RestPred does not use the LET identifiers; move outside of the LET !
1315 -> Result = select([b(select_when(RestPred,SelectBody),subst,[generated|I])]),
1316 SelectBody = b(let(LetIDs,LetDefPred,Subst),subst,[generated|I])
1317 ; is_truth(RestNotUsingIds)
1318 % RestPred uses LET identifiers in all conjuncts; move inside LET
1319 -> Result = let(LetIDs,LetDefPred,LetBody),
1320 LetBody = b(select([b(select_when(RestPred,Subst),subst,[])]),subst,[generated|I])
1321 ;
1322 % we would need to generate an outer and inner select; transformation probably not worth it
1323 fail
1324 )
1325 ; is_truth(RestUsingIds)
1326 % RestPred does not use LET identifiers move outside of LET
1327 -> Result = any(RestIds,RestPred,SelectBody),
1328 SelectBody = b(let(LetIDs,LetDefPred,Subst),subst,[generated|I])
1329 ; is_truth(RestNotUsingIds)
1330 % RestPred uses LET identifiers in all conjuncts; move inside LET
1331 -> Result = let(LetIDs,LetDefPred,LetBody),
1332 LetBody = b(any(RestIds,RestPred,Subst),subst,I)
1333 ;
1334 % we would need to generate an outer and inner any; transformation probably not worth it
1335 fail
1336 )
1337 ),
1338 !. %,translate:print_subst(b(Result,subst,[])),nl.
1339 cleanup_post_with_path(operation(TName,Res,Params,Body),Type,Info,
1340 operation(TName,Res,Params,NewBody),Type,Info,single/lts_min_guard_splitting,Path) :-
1341 get_preference(ltsmin_guard_splitting,true), % used also to checkpge_algo:is_pge_opt_on,
1342 Path = [path_arg(top_level(operation_bodies),_)], % only apply at top-level and TO DO: only for top-most machine !!
1343 % TO DO: also apply for Event-B models
1344 get_texpr_id(TName,Name),
1345 (get_operation_propositional_guards(Name,Res,Params,Body,Guards,RestBody)
1346 -> true
1347 ; add_warning(ltsmin_guard_splitting,'Cannot extract guard for:',Name),fail),
1348 Guards \= [],
1349 conjunct_predicates_with_pos_info(Guards,G),
1350 get_texpr_info(Body,BInfo),
1351 NewBody = b(precondition(G,RestBody),subst,[prob_annotation('LTSMIN-GUARD')|BInfo]), % a SELECT would be more appropriate
1352 (debug_mode(off) -> true
1353 ; format('Extracting LTS Min guard for ~w~n',[Name]),translate:print_subst(NewBody),nl).
1354 cleanup_post_with_path(exists(Ids,B),Type,OInfo,exists(Ids,B),Type,NInfo,single/invalidate_used_ids,_Path) :-
1355 delete(OInfo,used_ids(_),NInfo). % the changes done by cleanups, e.g, removing unused predicates can affect used_ids
1356 cleanup_post_with_path(forall(Ids,LHS,RHS),Type,OInfo,forall(Ids2,LHS,RHS),Type,NInfo,single/invalidate_used_ids,_Path) :-
1357 infer_seq_types_for_tids(Ids,LHS,Ids2), % for test 2009 it could be good to look also at RHS if it is equivalence,...
1358 delete(OInfo,used_ids(_),NInfo). % ditto
1359 cleanup_post_with_path(OExpr,OType,OInfo,NExpr,NType,NInfo,Mode/Rule,Path) :-
1360 get_preference(optimize_ast,true),
1361 cleanup_post_ne_with_path(OExpr,OType,OInfo,NExpr,NType,NInfo,Mode/Rule,Path).
1362 cleanup_post_with_path(OExpr,OType,OInfo,NExpr,NType,NInfo,Mode/Rule,_Path) :-
1363 ? cleanup_post_essential(OExpr,OType,OInfo,NExpr,NType,NInfo,Mode/Rule).
1364
1365 % delete an assignment from a substitution
1366 delete_assignment(b(assign(LHS,RHS),subst,Info),ID,IDRHS,b(RES,subst,Info)) :-
1367 nth1(Pos,LHS,ID,RestLHS),
1368 nth1(Pos,RHS,IDRHS,RestRHS),
1369 (RestLHS = [] -> RES = skip ; RES = assign(RestLHS,RestRHS)).
1370 % TO DO: also deal with parallel and possibly other constructs assign_single_id,...
1371
1372 %unguarded(path_arg(sequence/1,1)). % first argument of sequence is not guarded
1373 ?unguarded(path_arg(X,_)) :- unguarded_aux(X).
1374 unguarded_aux(top_level(_)).
1375 unguarded_aux(operation/4).
1376 unguarded_aux(parallel/1).
1377 unguarded_aux(var/2).
1378 unguarded_aux(let/3).
1379 % what about choice/2 ??
1380
1381 :- load_files(library(system), [when(compile_time), imports([environ/2])]).
1382
1383
1384 % ---------------------
1385
1386 % now the rules which do not need the Path
1387
1388 % first check for a few expressions that never need to be optimised, rewritten:
1389 cleanup_post_essential(E,_,_,_,_,_,_) :- never_transform_or_optimise(E),!,fail.
1390
1391 cleanup_post_essential(Expr,Type,I,Expr,Type,I2,single/remove_erroneous_info_field) :-
1392 \+ ground(I),!,
1393 functor(Expr,F,_N),
1394 add_internal_error('Information field not ground: ',I:F),
1395 I2=[].
1396 cleanup_post_essential(comprehension_set(Ids,E),Type,I,comprehension_set(Ids,E),Type,I,single/sanity_check) :-
1397 get_preference(prob_safe_mode,true),
1398 get_texpr_ids(Ids,UnsortedIds),sort(UnsortedIds,SIds),
1399 \+ same_length(UnsortedIds,SIds),
1400 add_error(cleanup_post,'Identifier clash in comprehension set: ',UnsortedIds),
1401 print(E),nl,
1402 fail.
1403
1404
1405 cleanup_post_essential(lambda(Ids,Pred,Expr),Type,I,
1406 comprehension_set(CompIds,CompPred),Type,NewInfo,multi/remove_lambda) :- !,
1407 unify_types_strict(Type,set(couple(_ArgType,ResType))),
1408 get_unique_id_inside('_lambda_result_',Pred,Expr,ResultId),
1409 def_get_texpr_id(Result,ResultId),
1410 get_texpr_type(Result,ResType),
1411 get_texpr_info(Result,[lambda_result(ResultId)]),
1412 append(Ids,[Result],CompIds),
1413 Equal = b(equal(Result,Expr),pred,[prob_annotation('LAMBDA-EQUALITY')|EqInfo]),
1414 extract_important_info_from_subexpression(Expr,EqInfo), % mark equality with wd condition if Expr has wd condition
1415 (member(prob_annotation('SYMBOLIC'),EqInfo)
1416 -> % something like %p.(p : BOOL|%t.(t : NATURAL|t .. t + 7))
1417 (debug_mode(off) -> true
1418 ; write('Marking lambda as symbolic because result is symbolic: '),print_bexpr(Expr),nl),
1419 add_info_if_new(I,prob_annotation('SYMBOLIC'),NewInfo)
1420 ? ; infinite_or_symbolic_domain_for_lambda(Ids,Pred,Kind)
1421 -> % something like %t.(t : NATURAL|t .. t + 7)
1422 (debug_mode(off) -> true
1423 ; get_texpr_ids(Ids,AIds),
1424 format('Marking lambda over ~w as symbolic because the domain is ~w: ',[AIds,Kind]),
1425 print_bexpr(Pred),nl),
1426 add_info_if_new(I,prob_annotation('SYMBOLIC'),NewInfo)
1427 ; NewInfo = I),
1428 conjunct_predicates_with_pos_info(Pred,Equal,CompPred0),
1429 add_texpr_infos(CompPred0,[prob_annotation('LAMBDA')],CompPred). % checked e.g. in is_converted_lambda_closure
1430 %print_bexpr(b(comprehension_set(CompIds,CompPred),Type,NewInfo)),nl.
1431
1432 cleanup_post_essential(reflexive_closure(Rel),Type,I, UNION,Type,NewInfo,multi/remove_reflexive_closure) :- !,
1433 NewInfo = [was(reflexive_closure)|I],
1434 safe_create_texpr(closure(Rel),Type,I,CL),
1435 UNION = union(b(event_b_identity,Type,IdInfo), CL), % closure(R) = id \/ closure1(R)
1436 (is_infinite_ground_type(Type) -> IdInfo = [prob_annotation('SYMBOLIC')|I] ; IdInfo =I),
1437 (debug_mode(on) -> print('Rewriting closure to: '), print_bexpr(b(UNION,Type,[])),nl ; true).
1438 cleanup_post_essential(evb2_becomes_such(Ids,Pred),subst,I,becomes_such(Ids,Pred2),subst,I,multi/ev2_becomes_such) :-
1439 % we translate a Classical-B becomes_such with id -> id$0, id' -> id
1440 % classical B: Dec = BEGIN level : (level>=0 & level> level$0-5 & level < level$0) END
1441 % Event-B: level'>= 0, level' > level-5 ...
1442 !,
1443 prime_identifiers(Ids,PIds),
1444 maplist(gen_rename,PIds,Ids,RenameList1), % id' -> id
1445 prime_identifiers0(Ids,PIds0),
1446 maplist(gen_rename,Ids,PIds0,RenameList2), % id -> id$0
1447 append(RenameList1,RenameList2,RenameList),
1448 rename_bt(Pred,RenameList,Pred2),
1449 (debug_mode(off) -> true
1450 ; format('Converting Event-B becomes_such: ',[]), print_bexpr(Pred2),nl).
1451 cleanup_post_essential(successor,Type,I,Compset,Type,[was(successor)|I],multi/successor) :- !,
1452 % translation of succ
1453 pred_succ_compset(add,Compset).
1454 cleanup_post_essential(rev(A),string,I,ExtFunCall,string,[was(rev)|I],multi/rev_for_string) :- !,
1455 % translation of rev for STRINGs, this can only occur when allow_sequence_operators_on_strings is true
1456 ExtFunCall = external_function_call('STRING_REV',[A]).
1457 cleanup_post_essential(concat(A,B),string,I,ExtFunCall,string,[was(concat)|I],multi/concat_for_string) :- !,
1458 % translation of concat (^) for STRINGs, can only occur when allow_sequence_operators_on_strings is true
1459 construct_string_append(A,B,ExtFunCall).
1460 cleanup_post_essential(general_concat(A),string,I,ExtFunCall,string,[was(general_concat)|I],multi/concat_for_string) :-
1461 !, % translation of conc for seq(STRING) --> STRING, can only occur when allow_sequence_operators_on_strings is true
1462 ExtFunCall = external_function_call('STRING_CONC',[A]).
1463 cleanup_post_essential(size(A),integer,I,ExtFunCall,integer,[was(A)|I],multi/concat_for_string) :-
1464 get_texpr_type(A,string),!,
1465 % translation of size for STRING, this can only occur when allow_sequence_operators_on_strings is true
1466 ExtFunCall = external_function_call('STRING_LENGTH',[A]).
1467 cleanup_post_essential(predecessor,Type,I,Compset,Type,[was(predecessor)|I],multi/predecessor) :- !,
1468 % translation of pred
1469 pred_succ_compset(minus,Compset).
1470 cleanup_post_essential(becomes_such(Ids1,Pred),subst,I,becomes_such(Ids2,Pred),subst,I,single/becomes_such) :- !,
1471 annotate_becomes_such_vars(Ids1,Pred,Ids2).
1472 cleanup_post_essential(Expr,Type,I,Expr,Type,[contains_wd_condition|I],multi/possibly_undefined) :-
1473 % multi: rule can only be applied once anyway, no need to check
1474 nonmember(contains_wd_condition,I),
1475 % print(' - CHECK WD: '), print_bexpr(Expr),nl, %%
1476 is_possibly_undefined(Expr),!,
1477 %% print('CONTAINS WD: '), print_bexpr(Expr),nl, %%
1478 %(translate:translate_bexpression(Expr,'{min(xunits)}') -> trace ; true),
1479 true.
1480 % if a substitution has a sub-expression that is a substitution with that refers to the original
1481 % value of a variable, we mark this substitution, too.
1482
1483 % If the substitution of an operation contains a while whose invariant contains references x$0
1484 % to the original value of a variable x, we must insert a LET substitution to preserve the original value.
1485 cleanup_post_essential(operation(Id,Results,Args,Body),Type,I,operation(Id,Results,Args,NewBody),Type,I,single/refers_to_old_state_let) :-
1486 get_texpr_info(Body,BodyInfo),
1487 memberchk(refers_to_old_state(References),BodyInfo),!,
1488 create_equalities_for_let(References,Ids,Equalities),
1489 conjunct_predicates_with_pos_info(Equalities,P),
1490 insert_let(Body,Ids,P,NewBody).
1491 cleanup_post_essential(Subst,subst,I,Subst,subst,NI,single/refers_to_old_state) :-
1492 safe_syntaxelement_det(Subst,Subs,_,_,_),
1493 % check if a child contains the refers_to_old_state flag
1494 findall(Reference, (member(Sub,Subs),
1495 get_texpr_info(Sub,SubInfo),
1496 memberchk(refers_to_old_state(References),SubInfo),
1497 member(Reference,References)), ReferedIds),
1498 ReferedIds \= [],
1499 !,
1500 sort(ReferedIds,SortedIds),
1501 NI = [refers_to_old_state(SortedIds)|I].
1502
1503 cleanup_post_essential(let_predicate([],[],TExpr),Type,Iin,Expr,Type,Iout,multi/remove_let_predicate) :- !,
1504 % same as above, just for predicates
1505 get_texpr_expr(TExpr,Expr),
1506 get_texpr_info(TExpr,I),
1507 % The next is done to prevent removing position information (in case of Event-B invariants, theorems,...)
1508 propagate_pos_info_if_useful(I,Iin,Iout).
1509
1510 cleanup_post_essential(OExpr,OType,OInfo,NExpr,NType,NInfo,Mode/Rule) :-
1511 get_preference(optimize_ast,true),
1512 ? cleanup_post(OExpr,OType,OInfo,NExpr,NType,NInfo,Mode/Rule).
1513
1514 % moved these rule below the simplifciation rules above to avoid re-computing used_id infos
1515 cleanup_post_essential(forall(Ids,Lhs,Rhs),pred,IOld,Res,pred,INew,single/forall_used_identifier) :-
1516 reconstruct_forall(Ids,Lhs,Rhs,IOld, b(Res,pred,INew)).
1517 cleanup_post_essential(exists(Ids,P),pred,IOld,exists(Ids,NP),pred,ResInfo,single/exists_used_identifier) :-
1518 inner_predicate_level_optimizations(P,NP),
1519 add_used_identifier_info(Ids,NP,IOld,INew0),
1520 add_used_ids_defined_by_equality(Ids,NP,INew0,INew),
1521 %print('* POST => '),print_bexpr(b(exists(Ids,NP),pred,INew)),nl,
1522 %print(' INFO=> '),print(INew),nl,
1523 % TO DO: also compute which identifiers are worth waiting for; do not wait for res in #x.(x:E & ... & res=min(f(x)))
1524 add_removed_typing_info(INew,ResInfo).
1525 cleanup_post_essential(Construct,Type,I,NewConstruct,Type,I,single/detect_partitions) :-
1526 contains_predicate(Construct,Type,Pred,NewConstruct,NewPred),!, % something like a select or other substitution
1527 predicate_level_optimizations(Pred,NewPred).
1528 cleanup_post_essential(Construct,Type,I,NewConstruct,Type,I,single/detect_partitions2) :-
1529 contains_predicates(Construct,Type,Preds,NewConstruct,NewPreds),!,
1530 maplist(predicate_level_optimizations,Preds,NewPreds) % TO DO: do CSE together (in some cases) !
1531 . %, (Preds=NewPreds -> true ; print('Found partitions: '), translate:print_subst(b(NewConstruct,Type,I)),nl).
1532
1533 construct_inner_forall(Ids,LHS,RHS,OldInfo,Res) :-
1534 delete(OldInfo,used_ids(_),I1), % we construct a changed forall construct; old used_ids info possibly incorrect
1535 reconstruct_forall(Ids,LHS,RHS,I1,Res).
1536 % use when in cleanup_post you construct a forall which will not be at the top-level
1537 % (meaning that the above essential rules will not run)
1538 reconstruct_forall(Ids,LHS,RHS,OldInfo, b(Res,pred,NewInfo)) :-
1539 inner_predicate_level_optimizations(LHS,NLhs),
1540 inner_predicate_level_optimizations(RHS,NRhs),
1541 construct_forall_opt(Ids,NLhs,NRhs,OldInfo, Res,NewInfo).
1542
1543 construct_forall_opt(IDs,NLhs,NRhs,Info, Res,NewInfo) :-
1544 (is_truth(NRhs) ; is_falsity(NLhs)),!,
1545 add_hint_message(remove_useless_assign,'Removing useless universal quantification','',Info),
1546 Res= truth, NewInfo = [was(forall(IDs,NLhs,NRhs))|Info].
1547 % TO DO: is the following rule useful ?: will require adapting test 510 output file
1548 % triggers e.g. for test 1447
1549 %construct_forall_opt([TID],LHS,RHS,Info, Res,NewInfo) :- % !x. (x:SetA => x:SetB) ---> SetA <: SetB
1550 % is_valid_id_member_check(LHS,TID,SetA), is_valid_id_member_check(RHS,TID,SetB),
1551 % !,
1552 % (debug_mode(off) -> true
1553 % ; format('Replacing forall ~w by subset: ',[TID]), print_bexpr(b(subset(SetA,SetB),pred,Info)),nl
1554 % ),
1555 % Res = subset(SetA,SetB), NewInfo = [was(forall)|Info].
1556 construct_forall_opt(Ids,NLhs,NRhs,OldInfo, forall(Ids,NLhs,NRhs),ResInfo) :-
1557 conjunct_predicates([NLhs,NRhs],P),
1558 add_used_identifier_info(Ids,P,OldInfo,Info),
1559 add_removed_typing_info(Info,ResInfo).
1560
1561 add_removed_typing_info(Info,ResInfo) :-
1562 (memberchk(removed_typing,Info) -> ResInfo = Info ; ResInfo = [removed_typing|Info]).
1563
1564 disjoint_ids(Ids1,Ids2) :-
1565 get_texpr_ids(Ids1,I1), sort(I1,SI1),
1566 get_texpr_ids(Ids2,I2), sort(I2,SI2),
1567 ord_disjoint(SI1,SI2).
1568
1569
1570 % translate concat(^) of strings:
1571 construct_string_append(A,B,ExtCall) :-
1572 is_string_conc_or_append(A,List,InfoA), !, % nested concat -> translate to STRING_CONC to enable optimisations
1573 append(List,[B],NewList),
1574 ExtCall = external_function_call('STRING_CONC',[S]),
1575 get_texpr_info(B,IB),merge_info(InfoA,IB,Info),
1576 S = b(sequence_extension(NewList),seq(string),Info).
1577 construct_string_append(A,B,external_function_call('STRING_APPEND',[A,B])).
1578
1579 is_string_conc_or_append(b(external_function_call(F,FArgs),_,Info),Args,Info) :-
1580 (F='STRING_APPEND' -> Args = FArgs
1581 ; F='STRING_CONC',
1582 FArgs = b(sequence_extension(SArgs),_,_)
1583 -> Args = SArgs).
1584
1585 % ---------------------
1586
1587 % non-essential post cleanup rules; only applied when optimize_ast is TRUE
1588
1589 % WITH PATH:
1590
1591 cleanup_post_ne_with_path(member(E,b(image(Rel,SONE),TypeImg,InfoImg)),pred,I,member(Couple,Rel),pred,I,multi/replace_image_by_member,Path) :-
1592 % x : Rel[{One}] => One|->x : Rel
1593 Path \= [path_arg(forall/3,1)|_], % not LHS of a forall
1594 %% do not do this if it is the LHS of a forall: !(aus2).( aus2 : helper[{mm}] => RHS) (as we no longer can apply the optimized set treatment for forall
1595 singleton_set_extension(SONE,One),
1596 %% Rel \= b(reverse(_),_,_), %% TO DO: maybe exclude this; here user maybe wants to explicitly compute image ?
1597 !,
1598 create_couple(One,E,Couple),
1599 (debug_mode(off) -> true
1600 ; print('Member of Image: '),print_bexpr(b(member(E,b(image(Rel,SONE),TypeImg,InfoImg)),pred,I)),nl,
1601 print(' replaced by: '),print_bexpr(b(member(Couple,Rel),pred,I)),nl
1602 ).
1603
1604
1605 % WITHOUT PATH:
1606
1607 cleanup_post(conjunct(b(truth,pred,I1),b(B,pred,I2)),pred,I0,B,pred,NewI,multi/remove_truth_conj1) :- !,
1608 include_important_info_from_removed_pred(I1,I2,I3), % ensure was,... information propagated
1609 add_important_info_from_super_expression(I0,I3,NewI).
1610 cleanup_post(conjunct(b(A,pred,I2),b(truth,pred,I1)),pred,I0,A,pred,NewI,multi/remove_truth_conj2) :- !,
1611 include_important_info_from_removed_pred(I1,I2,I3),
1612 add_important_info_from_super_expression(I0,I3,NewI).
1613 cleanup_post(conjunct(b(falsity,pred,I1),b(_,_,I2)),pred,I0,falsity,pred,NewI,multi/simplify_falsity_conj1) :- !,
1614 include_important_info_from_removed_pred(I2,I1,I3),
1615 add_important_info_from_super_expression(I0,I3,NewI).
1616 cleanup_post(conjunct(LHS,b(falsity,pred,I2)),pred,I0,falsity,pred,NewI,multi/simplify_falsity_conj2) :-
1617 always_well_defined_or_wd_reorderings_allowed(LHS), % we can only improve WD here,
1618 % also checks allow_improving_wd_mode preference
1619 !, % Note: ProB would treat falsity first anyway; so in principle this could be done always for solving
1620 get_texpr_info(LHS,I1),
1621 include_important_info_from_removed_pred(I1,I2,I3),
1622 add_important_info_from_super_expression(I0,I3,NewI).
1623 % we use FInfo: in case it has a was(.) field, e.g., for pretty printing and unsat core generation and unsatCore.groovy test
1624 cleanup_post(conjunct(AA,BB),pred,I,Res,pred,I,multi/modus_ponens) :-
1625 Impl = b(implication(A,B),pred,_),
1626 ((AA,BB) = (Impl,A2) ; (BB,AA) = (Impl,A2)),
1627 same_texpr(A,A2),
1628 % arises e.g., for predicates such as IF x:0..3 THEN y=2 ELSE 1=0 END ; works with simplify_falsity_impl3 rule
1629 % rewrite (A=>B) & A into (A&B)
1630 !,
1631 (debug_mode(off) -> true ; print('Modus Ponens: '),print_bexpr(A), print(' => '), print_bexpr(B),nl),
1632 Res = conjunct(A,B).
1633 %cleanup_post(conjunct(TLHS,P1),pred,_,LHS,pred,Info,multi/duplicate_pred) :-
1634 % % TO DO: implement an efficient version of this; currently very slow e.g. for test 293
1635 % b_interpreter:member_conjunct(P2,TLHS,_),
1636 % same_texpr(P1,P2),
1637 % TLHS = b(LHS,pred,Info),
1638 % print('remove_duplicate: '), print_bexpr(P1),nl.
1639 cleanup_post(conjunct(LHS,b(Comparison1,pred,_)),pred,I0,Result,pred,RInfo,multi/detect_interval1) :-
1640 % X <= UpBound & X >= LowBound <=> X : UpBound .. LowBound (particularly useful when CLPFD FALSE, causes problem with test 1771)
1641 % Note: x>18 & y<1024 & x<20 & y>1020 now works, it is bracketed ((()) & y>1020)
1642 get_preference(use_clpfd_solver,false),
1643 \+ data_validation_mode, % this rule may lead to additional enumerations
1644 get_leq_comparison(Comparison1,X,UpBound),
1645 ? select_conjunct(b(Comparison2,_,_),LHS,Prefix,Suffix),
1646 get_geq_comparison(Comparison2,X2,LowBound),
1647 same_texpr(X,X2),
1648 (always_well_defined_or_disprover_mode(UpBound)
1649 -> true
1650 ; % as we may move valuation earlier, we have to be careful
1651 % we check if Comparison2 is last conjunct; x=7 & x:8..(1/0) raises no WD error in ProB
1652 Suffix=[]
1653 ),
1654 !,
1655 create_interval_member(X,LowBound,UpBound,Member),
1656 append(Prefix,[Member|Suffix],ResultList),
1657 conjunct_predicates(ResultList,TResult),
1658 TResult = b(Result,pred,I1),
1659 add_important_info_from_super_expression(I0,I1,RInfo),
1660 (debug_mode(off) -> true ; print(' Detected interval membership (1): '),print_bexpr(b(Result,pred,RInfo)),nl).
1661 cleanup_post(conjunct(LHS,b(Comparison1,pred,_)),pred,I0,Result,pred,RInfo,multi/detect_interval2) :-
1662 % X >= LowBound & X <= UpBound <=> X : UpBound .. LowBound
1663 get_preference(use_clpfd_solver,false),
1664 \+ data_validation_mode, % this rule may lead to additional enumerations
1665 get_geq_comparison(Comparison1,X,LowBound),
1666 ? select_conjunct(b(Comparison2,_,_),LHS,Prefix,Suffix),
1667 get_leq_comparison(Comparison2,X2,UpBound),
1668 same_texpr(X,X2),
1669 (always_well_defined_or_disprover_mode(LowBound)
1670 -> true
1671 ; % as we may move valuation earlier, we have to be careful
1672 % we check if Comparison2 is last conjunct; x=7 & x:8..(1/0) raises no WD error in ProB
1673 Suffix=[]
1674 ),
1675 !,
1676 create_interval_member(X,LowBound,UpBound,Member),
1677 append(Prefix,[Member|Suffix],ResultList),
1678 conjunct_predicates(ResultList,TResult),
1679 TResult = b(Result,pred,I1),
1680 add_important_info_from_super_expression(I0,I1,RInfo),
1681 (debug_mode(off) -> true ; print(' Detected interval membership (2): '),print_bexpr(b(Result,pred,RInfo)),nl).
1682
1683 cleanup_post(disjunct(b(truth,pred,I1),_),pred,I0,truth,pred,I,multi/simplify_truth_disj1) :- !,
1684 add_important_info_from_super_expression(I0,I1,I).
1685 cleanup_post(disjunct(b(A,pred,I1),b(falsity,pred,_)),pred,I0,A,pred,I,multi/remove_falsity_disj1) :- !,
1686 add_important_info_from_super_expression(I0,I1,I).
1687 cleanup_post(disjunct(b(falsity,pred,_),b(B,pred,I1)),pred,I0,B,pred,I,multi/remove_falsity_disj2) :- !,
1688 add_important_info_from_super_expression(I0,I1,I).
1689 cleanup_post(disjunct(Expr1,b(truth,pred,I1)),pred,I0,truth,pred,I,multi/simplify_truth_disj2) :-
1690 always_well_defined_or_wd_reorderings_allowed(Expr1), !, % we can only improve WD here
1691 add_important_info_from_super_expression(I0,I1,I).
1692 cleanup_post(disjunct(P1,NegP1),pred,Info,truth,pred,Info,multi/tautology_disjunction) :-
1693 % P1 or not(P1) == truth (was created by Rodin for WD)
1694 ? is_negation_of(P1,NegP1),
1695 always_well_defined_or_disprover_mode(P1),
1696 (debug_mode(off) -> true
1697 ; format('Detected useless disjunction (tautology): ',[]),print_bexpr(b(disjunct(P1,NegP1),pred,Info)),nl).
1698 cleanup_post(disjunct(Equality1,Equality2),pred,I,New,pred,I,multi/rewrite_disjunct_to_member1) :-
1699 ? identifier_equality(Equality2,ID,_,Expr2),
1700 always_well_defined_or_disprover_mode(Expr2),
1701 get_texpr_type(Expr2,Type2),
1702 type_contains_no_sets(Type2), % we do not want to generate sets of sets, or worse sets with infinite sets (x=NATURAL1 or ...) which cannot be converted to AVL
1703 ? identifier_equality(Equality1,ID,TID,Expr1),
1704 % Rewrite (ID = Expr1 or ID = Expr2) into ID: {Expr1,Expr2} ; good if FD information can be extracted for ID
1705 % But: can be bad for reification, in particular when set extension cannot be computed fully
1706 % TO DO: also deal with ID : {Values} and more general extraction of more complicated disjuncts
1707 % TO DO: also apply for implication (e.g., ID /= E1 => ID=E2)
1708 !,
1709 construct_set_extension(Expr1,Expr2,SetX), % TODO: check if fulltype? BOOL already treated in tautology_disjunction
1710 New=member(TID,SetX),
1711 (debug_mode(off) -> true
1712 ; format('Rewrite disjunct (1) ~w: ',[ID]),print_bexpr(SetX),nl).
1713 cleanup_post(disjunct(LHS1,LHS2),pred,I,New,pred,I,multi/rewrite_disjunct_to_member2) :-
1714 ? id_member_of_set_extension(LHS2,ID,TID1,LExpr2), % also detects equalities
1715 get_texpr_type(TID1,Type),
1716 get_preference(use_clpfd_solver,true),type_contains_fd_index(Type), % merging is potentially useful
1717 maplist(always_well_defined_or_disprover_mode,LExpr2), % TODO: check that merging makes sense, e.g., definite FD values or simple identfiiers
1718 id_member_of_set_extension(LHS1,ID,TID,LExpr1),
1719 % Rewrite (ID : {Expr1,...} or ID : {Expr2,...} into ID: {Expr1,Expr2}
1720 l_construct_set_extension(LExpr1,LExpr2,SetX),
1721 New=member(TID,SetX),
1722 (debug_mode(off) -> true
1723 ; format('Rewrite disjunct (2) ~w: ',[ID]),print_bexpr(SetX),nl).
1724 cleanup_post(disjunct(CEquality1,CEquality2),pred,IOld,New,pred,INew,multi/factor_common_pred_in_disjunction) :-
1725 % (x=2 & y=3) or (x=2 & y=4) -> x=2 & (y=3 or y=4) to improve constraint propagation
1726 ? factor_disjunct(CEquality1,CEquality2,IOld,New,INew),
1727 (debug_mode(off) -> true
1728 ; format('Factor disjunct: ',[]),print_bexpr(b(New,pred,INew)),nl).
1729 cleanup_post(implication(b(truth,pred,_),b(B,pred,I1)),pred,I0,B,pred,I,multi/remove_truth_impl1) :- !,
1730 add_important_info_from_super_expression(I0,I1,I).
1731 cleanup_post(implication(b(falsity,pred,I1),_),pred,I0,truth,pred,I,multi/simplify_falsity_impl1) :- !,
1732 add_important_info_from_super_expression(I0,I1,I).
1733 cleanup_post(implication(_,b(truth,pred,I1)),pred,I0,truth,pred,I,multi/simplify_truth_impl2) :- !,
1734 add_important_info_from_super_expression(I0,I1,I).
1735 cleanup_post(implication(P,b(falsity,pred,_)),pred,I,NotP,pred,[was(implication)|I],multi/simplify_falsity_impl3) :- !,
1736 create_negation(P,TNotP),
1737 (debug_mode(off) -> true ; print_bexpr(P), print(' => FALSE simplified'),nl),
1738 get_texpr_expr(TNotP,NotP).
1739 % TO DO: is the following rule useful ?:
1740 %cleanup_post(implication(A,b(implication(B,C),pred,_)),pred,IOld,
1741 % implication(AB,C),pred,IOld,single/replace_implication_by_and) :-
1742 % % (A => B => C <==> (A & B) => C
1743 % conjunct_predicates([A,B],AB),
1744 % (debug_mode(off) -> true ; print('Simplifying double implication: '), print_bexpr(b(implication(AB,C),pred,IOld)),nl).
1745 cleanup_post(equivalence(TA,b(B,pred,I1)),pred,I0,B,pred,I,multi/remove_truth_equiv1) :-
1746 is_truth(TA), !,
1747 add_important_info_from_super_expression(I0,I1,I).
1748 cleanup_post(equivalence(b(A,pred,I1),TB),pred,I0,A,pred,I,multi/remove_truth_equiv2) :-
1749 is_truth(TB), !,
1750 add_important_info_from_super_expression(I0,I1,I).
1751 % TO DO: more rules for implication/equivalence to introduce negations (A <=> FALSITY ---> not(A)) ?
1752 % detect certain tautologies/inconsistencies
1753 cleanup_post(lazy_let_pred(_ID,_,b(Sub,pred,I1)),pred,I0,Sub,pred,I,multi/remove_lazy_let_pred) :-
1754 (Sub=truth ; Sub=falsity), !,
1755 add_important_info_from_super_expression(I0,I1,I).
1756 cleanup_post(IFTHENELSE,T,_,Res,T,[was(ifthenelse)|NI],single/remove_if_then_else) :-
1757 explicit_if_then_else(IFTHENELSE,IF,THEN,ELSE),
1758 (is_falsity(IF) -> b(Res,_,NI)=ELSE
1759 ; is_truth(IF) -> b(Res,_,NI)=THEN
1760 ),
1761 (debug_mode(off) -> true
1762 ; print('Simplified IF-THEN-ELSE: '), print_bexpr(IF),nl).
1763 cleanup_post(member(X,B),pred,I,truth,pred,[was(member(X,B))|I],multi/remove_type_member) :-
1764 is_just_type(B),
1765 nonmember(label(_),I), % the user has explicitly labeled this conjunct
1766 !. % print('REMOVE: '),print_bexpr(b(member(X,B),pred,[])),nl, print(I),nl.
1767 %cleanup_post(member(X,SET),pred,I,greater_equal(X,TBound),pred,[was(member(X,SET))|I],multi/remove_type_member) :-
1768 % disabled at the moment: we need to adapt test 1383, 767, 1703, 1003
1769 % is_inf_integer_set_with_lower_bound(SET,Bound),
1770 % Replace x:NATURAL by x>=0 and x:NATURAL1 by x>=1 ; is usually much more efficient
1771 % note : removes virtual timeout in test 290
1772 % !,
1773 % TBound = b(integer(Bound),integer,[]), print('REPLACE: '),print_bexpr(b(member(X,SET),pred,I)),nl, print_bexpr(b(greater_equal(X,TBound),pred,I)),nl.
1774 cleanup_post(not_member(X,B),pred,I,falsity,pred,[was(not_member(X,B))|I],multi/remove_type_not_member) :-
1775 is_just_type(B),
1776 nonmember(label(_),I), % the user has explicitly labeled this conjunct
1777 !.
1778 cleanup_post(member(X,TSet),pred,I,equal(X,One),pred,I,multi/remove_member_one_element_set) :-
1779 singleton_set_extension(TSet,One),
1780 !,
1781 % X:{One} <=> X=One
1782 true. %,print('Introducing equality: '),print_bexpr(X), print(' = '), print_bexpr(One),nl.
1783 cleanup_post(member(X,b(Set,_,_)),pred,I,not_equal(X,One),pred,I,multi/remove_member_setdiff) :-
1784 Set = set_subtraction(MaximalSet,SONE),
1785 singleton_set_extension(SONE,One),
1786 definitely_maximal_set(MaximalSet),
1787 !, % x : INTEGER-{One} <=> x/=One
1788 (debug_mode(off) -> true
1789 ; print('Replacing member of set_subtraction: '), print_bexpr(MaximalSet), print(' - '), print_bexpr(SONE),nl).
1790 cleanup_post(not_member(X,TSet),pred,I,not_equal(X,One),pred,I,multi/remove_member_one_element_set) :-
1791 singleton_set_extension(TSet,One),
1792 !,
1793 % X/:{One} <=> X/=One
1794 true.
1795 cleanup_post(member(E,b(fin_subset(E2),_,_)),pred,I,finite(E),pred,I,multi/introduce_finite) :-
1796 (same_texpr(E,E2); is_just_type(E2)),!. % print(introduce(finite(E))),nl.
1797 cleanup_post(not_member(E,b(fin_subset(E2),_,_)),pred,I,NotFinite,pred,I,multi/introduce_not_finite) :-
1798 (same_texpr(E,E2); is_just_type(E2)),!,
1799 create_negation(b(finite(E),pred,I),TNotP), get_texpr_expr(TNotP,NotFinite).
1800 /* do we want need this rule ?:
1801 clenaup_post(member(b(couple(A,B),couple(TA,TB),IC),ID),pred,I,equal(A,B),pred,I,multi/replace_member_id) :-
1802 is_is_event_b_identity(ID), !.
1803 */
1804 cleanup_post(member(b(couple(A,B),couple(TA,TB),IC),b(reverse(Rel),_,_)),pred,I,member(ICouple,Rel),pred,I,multi/remove_reverse) :- !,
1805 % (A,B) : Rel~ ===> (B,A) : Rel
1806 % can be detrimental for performance when A is known and B is not and Rel is large
1807 \+ data_validation_mode,
1808 (debug_mode(off) -> true ; print('Removed inverse (~): '),print_bexpr(Rel),nl),
1809 ICouple = b(couple(B,A),couple(TB,TA),IC).
1810 cleanup_post(member(LHS,ITE),pred,I,Result,pred,I,multi/member_if_then_else) :-
1811 get_texpr_expr(ITE,if_then_else(IFPRED,THEN,ELSE)),
1812 (definitely_empty_set(ELSE)
1813 -> A=THEN, P = IFPRED
1814 ; definitely_empty_set(THEN)
1815 -> A=ELSE, create_negation(IFPRED,P)
1816 ),
1817 % x: IF P THEN A ELSE {} END --> P & x:A
1818 % x: IF P THEN {} ELSE A END --> not(P) & x:A
1819 % appears in some generated Caval machines
1820 % x: IF a=TRUE THEN {11} ELSE {} END & x>11 can now be solved deterministically
1821 MEM = b(member(LHS,A),pred,I),
1822 conjunct_predicates([P,MEM],TR),
1823 (debug_mode(off) -> true ; print('Replace member of if-then-else by: '),print_bexpr(TR),nl),
1824 get_texpr_expr(TR,Result).
1825 cleanup_post(member(LHS,Comprehension),pred,I,Result,pred,NewInfo,multi/remove_member_comprehension) :-
1826 Comprehension = b(ComprSet,_,_),
1827 is_comprehension_set(ComprSet,[TID],Body),
1828 get_texpr_id(TID,ID),
1829 % LHS:{x|P(x)} ==> P(LHS),
1830 % TODO: treat LHS1|->LHS2 : {x,y|P(x,y)} ==> P(LHS1,LHS2) ; see tid_matches_paras and is_member_closure
1831 replace_id_by_expr_with_count(Body,ID,LHS,TResult,Count),
1832 % rewrite could duplicate LHS: not an issue in CSE mode; optimization relevant in normalize_ast mode
1833 ? is_replace_id_by_expr_ok(LHS,ID,Count,remove_member_comprehension),
1834 !,
1835 % could introduce LET if necessary because ID occurs multiple times (Count>1)
1836 get_texpr_expr(TResult,Result),
1837 get_texpr_info(TResult,I1),
1838 add_important_info_from_super_expression(I,I1,NewInfo),
1839 (debug_mode(off) -> true ; print('Remove element of comprehension_set: '),print_bexpr(Comprehension),nl,
1840 format(' rewriting to (~w): ',[Count]),print_bexpr(TResult),nl).
1841 cleanup_post(not_member(LHS,Comprehension),pred,I,Result,pred,I,multi/remove_not_member_comprehension) :-
1842 Comprehension = b(ComprSet,_,_),
1843 is_comprehension_set(ComprSet,[TID],Body),
1844 get_texpr_id(TID,ID),
1845 % LHS/:{x|P(x)} ==> not(P(LHS))
1846 replace_id_by_expr_with_count(Body,ID,LHS,TResult,Count),
1847 % rewrite could duplicate LHS: not an issue in CSE mode; optimization relevant in normalize_ast mode
1848 ? is_replace_id_by_expr_ok(LHS,ID,Count,remove_not_member_comprehension),
1849 !,
1850 Result = negation(TResult),
1851 (debug_mode(off) -> true ; print('Remove not element of comprehension_set: '),print_bexpr(Comprehension),nl,
1852 format(' rewriting to (~w): not(',[Count]),print_bexpr(TResult),print(')'),nl).
1853 cleanup_post(comprehension_set(Ids,Body),Type,I,NewExpr,Type,I2,Rule) :-
1854 ? cleanup_comprehension_set(Ids,Body,Type,I,NewExpr,I2,Rule),
1855 !.
1856 cleanup_post(subset(A,B),pred,I,truth,pred,[was(subset(A,B))|I],multi/remove_type_subset) :-
1857 is_just_type(B),
1858 nonmember(label(_),I), % the user has explicitly labeled this conjunct
1859 !.
1860 cleanup_post(not_subset(A,B),pred,I,falsity,pred,[was(not_subset(A,B))|I],multi/remove_type_not_subset) :-
1861 is_just_type(B),
1862 nonmember(label(_),I), % the user has explicitly labeled this conjunct
1863 !.
1864 cleanup_post(SUB,pred,I,NewPred,pred,[generated_conjunct|I],multi/replace_subset_by_element) :-
1865 is_subset(SUB,A,B),
1866 is_set_extension(A,List),
1867 !, % for sequence extension we don't need this as the interpreter knows exactly the cardinality of a sequence_extension ?
1868 % applying rule {x1,x2,...} <: B <=> x1:B & x2:B & ...
1869 maplist(gen_member_predicates(B),List,Conjuncts),
1870 conjunct_predicates(Conjuncts,TNewPred),
1871 % print('detected subset-member rule: '),print_bexpr(TNewPred),nl,
1872 get_texpr_expr(TNewPred,NewPred).
1873 cleanup_post(SUB,pred,I,NewPred,pred,[generated_conjunct|I],multi/replace_union_subset) :-
1874 is_subset(SUB,A,B),
1875 % mark conjunct as generated: used e.g. by flatten_conjunct in predicate_evaluator
1876 get_texpr_expr(A,union(_,_)),!,
1877 % applying rule A1 \/ A2 <: B <=> A1 <: B & A2 <: B
1878 % could be detrimental if checking that something is an element of B is expensive
1879 extract_unions(A,As),
1880 findall(Subi,(member(Ai,As),safe_create_texpr(subset(Ai,B),pred,I,Subi)),Conj), % we could try and re-run clean-up ? safe_create_texpr will ensure WD info set
1881 conjunct_predicates(Conj,TNewPred),
1882 get_texpr_expr(TNewPred,NewPred).
1883 cleanup_post(Comp,pred,I,SComp,pred,I,multi/simplify_cse_comparison) :-
1884 % simplify comparison operations; can result in improved constraint propagation
1885 % e.g., ia + CSE1 * 2 > ia + fa <=> CSE1 * 2 > fa for Setlog/prob-ttf/qsee-TransmitMemoryDumpOk21_SP_3.prob
1886 comparison(Comp,A,B,SComp,SA,SB),
1887 simplify_comparison_terms(A,B,SA,SB),!,
1888 (debug_mode(off) -> true
1889 ; print('Simplified: '),print_bexpr(b(Comp,pred,I)),
1890 print(' <=> '),print_bexpr(b(SComp,pred,I)),nl).
1891 cleanup_post(EMPTYSET,T,I,empty_set,T,[was(EMPTYSET)|I],multi/detect_emptyset) :- EMPTYSET \= empty_set,
1892 definitely_empty_set(b(EMPTYSET,T,I)),
1893 (debug_mode(off) -> true
1894 ; print('Detected empty set: '), print_bexpr(b(EMPTYSET,T,I)),nl).
1895 cleanup_post(equal(A,B),pred,I,truth,pred,[was(equal(A,B))|I],multi/remove_equality) :-
1896 same_texpr(A,B),always_well_defined_or_disprover_mode(A),!. % ,print(removed_equal(A,B)),nl.
1897 cleanup_post(equal(A,B),pred,I,equal(A2,B2),pred,I,multi/simplify_equality) :-
1898 simplify_equality(A,B,A2,B2).
1899 cleanup_post(not_equal(A,B),pred,I,not_equal(A2,B2),pred,I,multi/simplify_inequality) :-
1900 simplify_equality(A,B,A2,B2).
1901 cleanup_post(equal(A,B),pred,I,falsity,pred,[was(equal(A,B))|I],multi/remove_equality_false) :-
1902 different_texpr_values(A,B),!. %,print(removed_equal_false(A,B)),nl.
1903 cleanup_post(equal(A,B),pred,I,greater(Low,Up),pred,I,multi/remove_equality) :-
1904 % Low..Up = {} <=> Low>Up % is also handled by constraint solver; but other simplifications can apply here
1905 ? (definitely_empty_set(B), is_interval(A,Low,Up) ;
1906 definitely_empty_set(A), is_interval(B,Low,Up)),!,
1907 (debug_mode(off) -> true
1908 ; print('Simplified: '), print_bexpr(b(equal(A,B),pred,I)), print(' <=> '),
1909 print_bexpr(b(greater(Low,Up),pred,I)),nl).
1910 cleanup_post(CardGt0Expr,pred,I,not_equal(X,EmptySet),pred,I,multi/remove_cardgt0) :-
1911 get_geq_comparison(CardGt0Expr,Card,One),
1912 % card(P) > 0 -> P\={} if wd guaranteed; also rewrites card(P) >= 1
1913 Card = b(card(X),integer,_), get_integer(One,1),
1914 finite_set_or_disprover_mode(X), % as we keep X, it is sufficient for X to be finite for the rule to be ok
1915 get_texpr_type(X,TX), get_texpr_info(One,I0),
1916 EmptySet = b(empty_set,TX,I0),!,
1917 (debug_mode(off) -> true ; print('Removed card(.) > 0 for set: '), print_bexpr(X), nl).
1918 cleanup_post(CardEq0Expr,pred,I,equal(X,EmptySet),pred,I,multi/remove_cardeq0) :-
1919 is_equality(b(CardEq0Expr,pred,I),Card,Zero),
1920 % card(P) = 0 -> P={} if wd guaranteed
1921 Card = b(card(X),integer,_), get_integer(Zero,0),
1922 finite_set_or_disprover_mode(X),
1923 get_texpr_type(X,TX), get_texpr_info(Zero,I0),
1924 EmptySet = b(empty_set,TX,I0),!,
1925 (debug_mode(off) -> true ; print('Removed card(.) = 0 for set: '), print_bexpr(X), nl).
1926 cleanup_post(CardLt1Expr,pred,I,equal(X,EmptySet),pred,I,multi/remove_cardlt1) :-
1927 get_leq_comparison(CardLt1Expr,Card,Zero),
1928 % card(P) <= 0 -> P={} if wd guaranteed; TODO: also detect card(P) < 1
1929 Card = b(card(X),integer,_), get_integer(Zero,0),
1930 finite_set_or_disprover_mode(X),
1931 get_texpr_type(X,TX), get_texpr_info(Zero,I0),
1932 EmptySet = b(empty_set,TX,I0),!,
1933 (debug_mode(off) -> true ; print('Removed card(.) <= 0 for set: '), print_bexpr(X), nl).
1934 cleanup_post(member(Card,Natural),pred,I,truth,pred,I,multi/remove_card_natural) :-
1935 % card(P) : NATURAL -> truth if wd guaranteed
1936 Card = b(card(X),integer,_),
1937 is_integer_set(Natural,'NATURAL'),
1938 always_well_defined_or_disprover_mode(Card),
1939 !,
1940 (debug_mode(off) -> true ; print('Removed card(.):NATURAL for set: '), print_bexpr(X), nl).
1941 cleanup_post(not_equal(A,B),pred,I,less_equal(Low,Up),pred,I,multi/remove_equality) :-
1942 % Low..Up \= {} <=> Low<=Up % is also handled by constraint solver; but other simplifications can apply here
1943 ? (definitely_empty_set(B), is_interval(A,Low,Up) ;
1944 definitely_empty_set(A), is_interval(B,Low,Up)),!,
1945 (debug_mode(off) -> true
1946 ; print('Simplified: '), print_bexpr(b(equal(A,B),pred,I)), print(' <=> '),
1947 print_bexpr(b(less_equal(Low,Up),pred,I)),nl).
1948 cleanup_post(equal(A,B),pred,I,equal(A,RLet),pred,I,single/detect_recursion) :-
1949 % "A" should be an identifier
1950 get_texpr_id(A,ID),
1951 % check if some side conditions are fulfilled where the recursion detection can be enabled
1952 recursion_detection_enabled(A,B,I),
1953 % A must be recursively used in B:
1954 find_recursive_usage(B,ID),
1955 % TO DO: also find mutual recursion !
1956 debug_println(9,recursion_detected(ID)),
1957 !, % create an recursive_let where the body is annotated to be symbolic
1958 get_texpr_type(B,Type), add_texpr_infos(B,[prob_annotation('SYMBOLIC')],B2),
1959 mark_recursion(B2,ID,B3),
1960 %print(marked_recursion(ID)),nl,
1961 safe_create_texpr(recursive_let(A,B3),Type,RLet).
1962 cleanup_post(recursive_let(TID,TBody),T,_,Body,T,I2,single/remove_recursive_let) :- get_texpr_id(TID,ID),
1963 \+ occurs_in_expr(ID,TBody),
1964 debug_println(19,removing_recursive_let(ID)), % required for test 1225
1965 TBody = b(Body,_,I2).
1966 cleanup_post(equal(A,B),pred,Info1,ResultExpr,pred,Info3,multi/simplify_bool_true_false) :-
1967 % simplify bool(X)=TRUE -> X and bool(X)=FALSE -> not(X)
1968 ( get_texpr_expr(A,convert_bool(X)), get_texpr_boolean(B,BOOLVAL)
1969 ;
1970 get_texpr_boolean(A,BOOLVAL),get_texpr_expr(B,convert_bool(X))
1971 ),
1972 get_texpr_info(X,Info2),
1973 add_important_info_from_super_expression(Info1,Info2,Info3),
1974 (BOOLVAL = boolean_true -> get_texpr_expr(X,ResultExpr) ;
1975 BOOLVAL = boolean_false -> create_negation(X,TNX), get_texpr_expr(TNX,ResultExpr)),
1976 !,
1977 (debug_mode(off) -> true
1978 ; format('Simplifying bool(.)=~w to ',[BOOLVAL]),translate:print_bexpr(b(ResultExpr,pred,Info3)),nl).
1979 %cleanup_post(equal(A,B),pred,Info1,equal(REL,CartProd),pred,Info1,multi/simplify_image) :-
1980 % cannot be applied yet; SETS not precompiled yet !
1981 % A = b(image(REL,SetExt),_,_),
1982 % % REL[{OneEl}] = B ----> REL = {OneEl}*B if OneEl is the only possible value
1983 % % such signature appear in Alloy generated code
1984 % SetExt = b(set_extension([_]),set(global(GlobalSetName)),_),
1985 % %bmachine:b_get_named_machine_set_calc(GlobalSetName,_,[_]),
1986 % b_global_set_cardinality(Type,1), % cannot be called yet; global sets not precompiled
1987 % !,
1988 % get_texpr_type(REL,RelType),
1989 % safe_create_texpr(cartesian_product(SetExt,B),RelType,CartProd),
1990 % format('Translating image for singleton set'), print_bexpr(b(equal(REL,CartProd),pred,[])),nl.
1991 cleanup_post(not_equal(A,B),pred,I,falsity,pred,[was(not_equal(A,B))|I],multi/remove_disequality) :-
1992 same_texpr(A,B),always_well_defined_or_disprover_mode(A),!. % ,print(removed_not_equal(A,B)),nl.
1993 % sometimes one uses & TRUE=TRUE to finish off guards, invariants, ...
1994 % exchange lambda expressions by a comprehension set
1995 % TO DO: also add rule for bool(X)=FALSE -> not(X)
1996 cleanup_post(not_equal(A,B),pred,I,truth,pred,[was(not_equal(A,B))|I],multi/remove_disequality_false) :-
1997 different_texpr_values(A,B),!. % ,print(removed_not_equal_false(A,B)),nl.
1998 cleanup_post(not_equal(A,B),pred,I,NewP,pred,[was(not_equal(A,B))|I],multi/not_disjoint_disequality) :-
1999 /* Set1 /\ Set2 /= {} <===> #(zz).(zz:Set1 & zz:Set2) */
2000 preferences:preference(use_smt_mode,true), /* currently this rewriting makes test 1112 fail; TO DO: investigate */
2001 definitely_empty_set(B),
2002 get_texpr_expr(A,intersection(Set1,Set2)),!,
2003 get_texpr_type(Set1,Set1Type), unify_types_strict(Set1Type,set(T)),
2004 ID = b(identifier('_zzzz_unary'),T,[generated]),
2005 ESet1 = b(member(ID,Set1),pred,[]),
2006 ESet2 = b(member(ID,Set2),pred,[]),
2007 create_exists_opt([ID],[ESet1,ESet2],NewPredicate),
2008 (debug_mode(off) -> true
2009 ; print('Transformed not disjoint disequality: '),print_bexpr(NewPredicate),nl),
2010 get_texpr_expr(NewPredicate,NewP).
2011 cleanup_post(equal(b(intersection(A,B),_,_),Empty),pred,I,not_equal(El1,El2),pred,[was(intersection)|I],multi/detect_not_equal) :-
2012 % {El1} /\ {El2} = {} --> El1 \= El2 (disjoint sets)
2013 singleton_set_extension(A,El1),
2014 singleton_set_extension(B,El2),
2015 definitely_empty_set(Empty).
2016 cleanup_post(equal(b(intersection(A,B),_,_),Empty),pred,I,NewDisequality,pred,[was(intersection)|I],multi/detect_disjoint_set_extensions) :-
2017 % {El1,...} /\ {El2,...} = {} --> El1 \= El2 & .... (disjoint sets)
2018 get_texpr_expr(A,set_extension(Els1)), length(Els1,Len1), Len1 < 20,
2019 get_texpr_expr(B,set_extension(Els2)), length(Els2,Len2), Len2 < 20,
2020 Len1 * Len2 < 100,
2021 definitely_empty_set(Empty),
2022 maplist(simple_expression,Els1), % avoid duplication of computation
2023 maplist(simple_expression,Els2), % ditto
2024 findall(NotEqual, (member(A1,Els1),member(B1,Els2),safe_create_texpr(not_equal(A1,B1),pred,NotEqual)),
2025 NotEquals),
2026 conjunct_predicates(NotEquals,TRes),
2027 (debug_mode(off) -> true
2028 ; write('Expanding disjoint set constraint: '),translate:print_bexpr(TRes),nl
2029 ),
2030 get_texpr_expr(TRes,NewDisequality).
2031 cleanup_post(greater(A,B),pred,I,Res,pred,[was(greater(A,B))|I],multi/eval_greater) :-
2032 get_integer(A,IA), get_integer(B,IB),
2033 (IA>IB -> Res = truth ; Res=falsity).
2034 cleanup_post(less(A,B),pred,I,Res,pred,[was(less(A,B))|I],multi/eval_less) :-
2035 get_integer(A,IA), get_integer(B,IB),
2036 (IA<IB -> Res = truth ; Res=falsity).
2037 cleanup_post(greater_equal(A,B),pred,I,Res,pred,[was(greater_equal(A,B))|I],multi/eval_greater_equal) :-
2038 get_integer(A,IA), get_integer(B,IB),
2039 (IA >= IB -> Res = truth ; Res=falsity).
2040 cleanup_post(less_equal(A,B),pred,I,Res,pred,[was(less_equal(A,B))|I],multi/eval_less_equal) :-
2041 get_integer(A,IA), get_integer(B,IB),
2042 (IA =< IB -> Res = truth ; Res=falsity).
2043 % TODO: similar things for less_real ?
2044 cleanup_post(CHOOSE,real,I,div_real(A,B),real,I,multi/replace_tla_real_division) :-
2045 is_tla_real_division(CHOOSE,A,B),
2046 % Detect TLA real division defined by Div_1(a, b) == CHOOSE({m|m:REAL & m*b=a})
2047 add_debug_message(ast_cleanup,'Translated TLA+ division to real division: ',b(div_real(A,B),real,I),I).
2048 cleanup_post(negation(A),pred,I,falsity,pred,[was(negation(A))|I],multi/remove_negation_truth) :-
2049 is_truth(A),!. % ,print(negation(A)),nl.
2050 cleanup_post(negation(A),pred,I,truth,pred,[was(negation(A))|I],multi/remove_negation_falsity) :-
2051 is_falsity(A),!. % ,print(negation(A)),nl.
2052 cleanup_post(convert_bool(A),boolean,I,Res,boolean,I,multi/remove_convert_bool) :-
2053 (is_truth(A) -> Res = boolean_true
2054 ; is_falsity(A) -> Res = boolean_false
2055 ; is_equality(A,LHS,BoolTRUE), get_texpr_boolean(BoolTRUE,boolean_true) % bool(LHS=TRUE) --> LHS
2056 -> get_texpr_expr(LHS,Res)
2057 ; A=not_equal(LHS,BoolFALSE), get_texpr_boolean(BoolFALSE,boolean_false) % bool(LHS/=FALSE) --> LHS
2058 -> get_texpr_expr(LHS,Res)
2059 ),!.
2060 cleanup_post(assertion_expression(Cond,_ErrMsg,Expr),_T,I0,BE,TE,IE,multi/remove_assertion_expression) :-
2061 is_truth(Cond),!,
2062 Expr = b(BE,TE,I1),
2063 add_important_info_from_super_expression(I0,I1,IE).
2064 cleanup_post(card(INTERVAL), T, I, Res, T, I, single/card_of_interval) :-
2065 % e.g., card(1..4) -> 4
2066 ? is_interval(INTERVAL, LowerBound, UpperBound),
2067 get_integer(LowerBound, L),
2068 number(L),
2069 get_integer(UpperBound, U),
2070 number(U),
2071 (L > U -> Card = 0 ; Card is 1+(U - L)),
2072 !,
2073 Res = integer(Card).
2074 cleanup_post(member(Empty,TPow),T,I,Res,T,I,single/empty_set_in_pow_subset) :-
2075 definitely_empty_set(Empty), % useful for z3 integration to prevent powerset constraint
2076 TPow = b(POW,_,_),
2077 (POW=pow_subset(_) -> RT=truth
2078 ; POW=fin_subset(_) -> RT=truth
2079 ; POW=pow1_subset(_) -> RT=falsity
2080 ; POW=fin1_subset(_) -> RT=falsity),
2081 always_well_defined_or_disprover_mode(TPow),
2082 !,
2083 Res = RT.
2084 cleanup_post(card(Empty),T,I,Res,T,I,single/card_singleton_set) :-
2085 Empty = b(empty_set,_,_), % useful for z3 integration to prevent cardinality constraint
2086 !,
2087 Res = integer(0).
2088 cleanup_post(card(SONE),T,I,Res,T,I,single/card_singleton_set) :-
2089 singleton_set_extension(SONE,One), % card({One}) = 1 ; useful for alloy2b
2090 always_well_defined_or_disprover_mode(One),
2091 !,
2092 Res = integer(1).
2093 cleanup_post(cartesian_product(A,B),T,I,Res,T,I,single/cartesian_product_to_pair) :-
2094 singleton_set_extension(A,El1),
2095 singleton_set_extension(B,El2), % {A}*{B} -> {A|->B} ; happens in Alloy translations a lot
2096 !,
2097 get_texpr_type(El1,T1), get_texpr_type(El2,T2),
2098 safe_create_texpr(couple(El1,El2),couple(T1,T2),Pair),
2099 Res = set_extension([Pair]).
2100 cleanup_post(image(Fun,Empty),T,I,empty_set,T,I,multi/image_empty_optimisation) :-
2101 definitely_empty_set(Empty),
2102 always_well_defined_or_wd_improvements_allowed(Fun),
2103 !,
2104 (debug_mode(off) -> true
2105 ; add_message(ast_cleanup,'Removing unnecessary image of empty set: ',Fun,I)).
2106 cleanup_post(union(A,B),T,I,Res,T,[add_element_to_set|I],multi/add_element_to_set) :- % multi: cycle check done in info field
2107 ? \+ member(add_element_to_set,I),
2108 ( singleton_set_extension(B,_El) -> Res = union(A,B)
2109 ; singleton_set_extension(A,_El) -> Res = union(B,A)),
2110 !. %,print(detected_add_singleton_element(_El)),nl.
2111 cleanup_post(union(A,B),T,I,Res,T,I,multi/union_empty_set) :-
2112 ( definitely_empty_set(A) -> get_texpr_expr(B,Res) % {} \/ B = B
2113 ; definitely_empty_set(B) -> get_texpr_expr(A,Res) % A \/ {} = A
2114 ),
2115 !.
2116 cleanup_post(intersection(A,B),T,I,empty_set,T,I,multi/intersection_empty_set) :-
2117 ( definitely_empty_set(A) -> true % A /\ {} = {}
2118 ; definitely_empty_set(B) -> true % {} /\ B = {}
2119 ),
2120 !.
2121 cleanup_post(set_subtraction(A,B),T,I,Res,T,I,multi/intersection_empty_set) :-
2122 ( definitely_empty_set(A) -> Res=empty_set % {} - B = {}
2123 ; definitely_empty_set(B) -> get_texpr_expr(A,Res) % A - {} = A
2124 ),
2125 !.
2126 cleanup_post(general_union(SetExt),Type,I0,Res,Type,Info,multi/general_union_set_extension) :- % union_generalized
2127 % union({a,b,c,...}) = a \/ b \/ c ...
2128 SetExt = b(set_extension(LIST),_,I1),
2129 add_important_info_from_super_expression(I0,I1,Info),
2130 % no need to apply rule if already transformed into avl in cleanup_pre, hence we do not call is_set_extension
2131 construct_union_from_list(LIST,Type,Info,TRes),
2132 !,
2133 (debug_mode(on) -> print('translated_general_union: '), print_bexpr(TRes),nl ; true),
2134 get_texpr_expr(TRes,Res).
2135 cleanup_post(general_intersection(SetExt),Type,I0,Res,Type,Info,multi/general_inter_set_extension) :- % inter_generalized
2136 % inter({a,b,c,...}) = a /\ b /\ c ...
2137 SetExt = b(set_extension(LIST),_,I1),
2138 add_important_info_from_super_expression(I0,I1,Info),
2139 % no need to apply rule if already transformed into avl in cleanup_pre, hence we do not call is_set_extension
2140 construct_inter_from_list(LIST,Type,Info,TRes),
2141 !,
2142 (debug_mode(on) -> print('translated_general_intersection: '), print_bexpr(TRes),nl ; true),
2143 get_texpr_expr(TRes,Res).
2144 cleanup_post(SUB,pred,I0,FORALL,pred,FInfo,multi/general_union_subset) :-
2145 is_subset(SUB,UNION,T),
2146 % union(S) <: T ===> !x.(x:S => x <: T)
2147 % currently: subsets of T may be generated, but it does not propagate well to S
2148 UNION = b(general_union(S),_,_),
2149 !,
2150 get_unique_id_inside('_zzzz_unary',S,T,ID),
2151 get_texpr_type(S,SType), is_set_type(SType,IDType),
2152 TID = b(identifier(ID),IDType,[generated]),
2153 safe_create_texpr(member(TID,S),pred,LHS),
2154 safe_create_texpr(subset(TID,T),pred,RHS),
2155 create_implication(LHS,RHS,NewForallBody),
2156 create_forall([TID],NewForallBody,TFORALL),
2157 TFORALL = b(FORALL,pred,I1),
2158 add_important_info_from_super_expression(I0,I1,FInfo),
2159 % see test 1854, and ProZ ROZ/model.tex
2160 (debug_mode(on) -> print('translated_general_union subset: '), print_bexpr(TFORALL),nl ; true).
2161 cleanup_post(size(Seq),integer,Info,Res,integer,Info,multi/size_append) :-
2162 get_texpr_expr(Seq,concat(A,B)),
2163 % size(A^B) = size(A)+size(B) useful e.g. for test 1306
2164 !,
2165 Res = add(b(size(A),integer,Info),b(size(B),integer,Info)).
2166 cleanup_post(concat(A,B),Type,I0,Seq,Type,[was(concat)|NewInfo],multi/concat_empty) :-
2167 ( definitely_empty_set(A) -> b(Seq,_,I1)=B
2168 ; definitely_empty_set(B) -> b(Seq,_,I1)=A
2169 ),!,
2170 add_important_info_from_super_expression(I0,I1,NewInfo).
2171 cleanup_post(concat(A,B),Type,I0,Seq,Type,[was(concat)|I0],multi/concat_singleton_seq) :-
2172 ( is_singleton_sequence(B,Element) -> Seq = insert_tail(A,Element)
2173 ; is_singleton_sequence(A,Element) -> Seq = insert_front(Element,B)
2174 ),!,
2175 debug_format(19,'Concat with singleton sequence detected~n',[]).
2176 cleanup_post(E,integer,I,Res,integer,[was(Operator)|I],multi/constant_expression) :-
2177 pre_compute_static_int_expression(E,Result),!,
2178 functor(E,Operator,_),
2179 % format('Precomputed: ~w for ',[Result]), translate:print_bexpr(b(E,integer,[])),nl,
2180 Res = integer(Result).
2181 cleanup_post(min(Interval),integer,I0,Res,integer,Info,multi/eval_min_interval) :-
2182 is_interval_or_singleton(Interval,Low,Up),
2183 get_integer(Low,L), number(L),
2184 get_integer(Up,U), number(U), L =< U, % non-empty interval
2185 debug_println(5,simplified_min_interval(L,U,L)),
2186 Res = integer(L), get_texpr_info(Low,I1),
2187 add_important_info_from_super_expression(I0,I1,Info).
2188 cleanup_post(max(Interval),integer,I0,Res,integer,Info,multi/eval_max_interval) :-
2189 is_interval_or_singleton(Interval,Low,Up),
2190 get_integer(Low,L), number(L),
2191 get_integer(Up,U), number(U), L =< U, % non-empty interval
2192 debug_println(5,simplified_max_interval(L,U,U)),
2193 Res = integer(U), get_texpr_info(Low,I1),
2194 add_important_info_from_super_expression(I0,I1,Info).
2195 cleanup_post(first(Seq),Type,I0,Res,Type,Info,multi/first_seq_extension) :-
2196 is_sequence_extension(Seq,List), List = [First|Rest],
2197 (Rest == [] -> true ; preferences:get_preference(disprover_mode,true)), % we may remove WD issue otherwise (TO DO: check if Rest contains any problematic elements)
2198 !,
2199 First = b(Res,Type,I1),
2200 add_important_info_from_super_expression(I0,I1,Info).
2201 cleanup_post(last(Seq),Type,I0,Res,Type,Info,multi/first_seq_extension) :-
2202 is_sequence_extension(Seq,List), List = [First|Rest],
2203 (Rest == [] -> true ; preferences:get_preference(disprover_mode,true)), % we may remove WD issue otherwise (TO DO: check if list contains any problematic elements)
2204 !,
2205 last([First|Rest],b(Res,Type,I1)),
2206 add_important_info_from_super_expression(I0,I1,Info).
2207 cleanup_post(function(Fun,Arg),Type,Info,New,Type,NewInfo,Rule) :-
2208 cleanup_post_function(Fun,Arg,Type,Info,New,NewInfo,Rule).
2209 % following rule deactivated until test 2580 failure analysed, see also diff of test 510
2210 %cleanup_post(range(DOMRES),Type,I, image(Rel,Set),Type,I,single/range_domres_image) :- % ran(S <| R) <==> R[S]
2211 % get_texpr_expr(DOMRES,domain_restriction(Set,Rel)),
2212 % add_debug_message(b_ast_cleanup,'Converted range and domain restriction to image: ',b(image(Rel,Set),Type,[]),I).
2213 % Note: we also have: dom(R |> S) <==> R~[S], but then we introduce the relational inverse operator
2214 cleanup_post(range(SETC),Type,I, comprehension_set(RangeIds2,NewCompPred),Type,I,single/range_setcompr) :-
2215 % translate ran({x1,...xn|P}) into {xn| #(x1,...).(P)} ; particularly interesting if x1... contains large datavalues (e.g., C_02_001.mch from test 1131)
2216 get_texpr_expr(SETC,comprehension_set(CompIds,CompPred)),
2217 get_domain_range_ids(CompIds,DomainIds,RangeIds), % print(range(CompIds,DomainIds,RangeIds)),nl,
2218 %\+((member(ID,RangeIds),get_texpr_id(ID,'_lambda_result_'))), % for test 612; maybe disable optimisation if memory consumption of variables small
2219 !, % TO DO: also detect patterns such as dom(dom( or ran(ran( ... [Done ??]
2220 ? rename_lambda_result_id(RangeIds,CompPred,RangeIds2,CompPred1),
2221 rename_lambda_result_id(DomainIds,CompPred1,DomainIds2,CompPred2),
2222 create_outer_exists_for_dom_range(DomainIds2,CompPred2,NewCompPred), % will mark the exists; so that during expansion we will treat it differently for enumeration
2223 (debug_mode(off) -> true
2224 ; print('Encode range as existential quantification: '), print_bexpr(NewCompPred),nl).
2225 cleanup_post(domain(SETC),Type,I, comprehension_set(DomainIds2,NewCompPred),Type,I,single/domain_setcompr) :-
2226 % translate dom({x1,...xn|P}) into {x1,..| #(xn).(P)} ; particularly interesting if xn contains large datavalues
2227 % used to fail test 306 ; fixed by allow_to_lift_exists annotation
2228 \+ data_validation_mode, % sometimes this optimisation is counter-productive for data_validation, problem: test 1945
2229 get_texpr_expr(SETC,comprehension_set(CompIds,CompPred)),
2230 get_domain_range_ids(CompIds,DomainIds,RangeIds), % print(domain(CompIds,DomainIds,RangeIds)),nl,
2231 % \+ (member(ID,CompIds),get_texpr_id(ID,'_lambda_result_')),
2232 % WE HAVE TO BE CAREFUL if xn = LAMBDA_RESULT ; TO DO rename like above for range
2233 % example from test 292: rel(fnc({x,y|x:1..10 & y:1..x})) = {x,y|x:1..10 & y:1..x}
2234 %\+ data_validation_mode, % test 1945 fails with WD errors if we disable this rule
2235 !,
2236 % TO DO: detect when closure is lambda; e.g., for e.g. dom(pred) = INTEGER in test 292 : split(CompIds,Args,Types), closures:is_lambda_value_domain_closure(Args,Types,B, DomainValue, _),; currently create_exists_opt deals with most of this
2237 rename_lambda_result_id(DomainIds,CompPred,DomainIds2,CompPred1),
2238 rename_lambda_result_id(RangeIds,CompPred1,RangeIds2,CompPred2),
2239 create_outer_exists_for_dom_range(RangeIds2,CompPred2,NewCompPred),
2240 (debug_mode(off) -> true
2241 ; get_texpr_ids(DomainIds,DIS), get_texpr_ids(RangeIds,RIS),
2242 ajoin(['Encode domain over ',DIS,' as existential quantification over ',RIS,' : '],Msg),
2243 add_message(ast_cleanup,Msg,NewCompPred,I)).
2244 cleanup_post(domain(SETC),Type,I, comprehension_set(DomainIds,RestPred),Type,I,single/domain_setcompr) :-
2245 % translate dom({x1,...xn|P & xn=E}) into {x1,..| P} ; particularly interesting if xn contains large datavalues
2246 get_texpr_expr(SETC,comprehension_set(CompIds,CompPred)),
2247 get_domain_range_ids(CompIds,DomainIds,RangeIds), % print(domain(CompIds,DomainIds,RangeIds)),nl,
2248 ? \+ (member(ID,CompIds),get_texpr_id(ID,'_lambda_result_')),
2249 conjunction_to_list(CompPred,Preds),
2250 RangeIds = [TId],
2251 get_sorted_ids(RangeIds,Blacklist),
2252 select_equality(TId,Preds,Blacklist,_Eq,_Expr,RestPreds,_,check_well_definedness), % We could do check_well_definedness only if preference set
2253 conjunct_predicates_with_pos_info(RestPreds,RestPred),
2254 not_occurs_in_predicate(Blacklist,RestPred),
2255 !,
2256 % TO DO: use create_optimized exists; also treat inner existential quantification and merge
2257 (debug_mode(off) -> true
2258 ; write('Encode domain of lambda abstraction: '),print_bexpr(RestPred),nl).
2259 cleanup_post(precondition(TP,TS),subst,I0,S,subst,Info,multi/remove_triv_precondition) :-
2260 % remove trivial preconditions
2261 get_texpr_expr(TP,truth),!,
2262 get_texpr_expr(TS,S),get_texpr_info(TS,I1),
2263 add_important_info_from_super_expression(I0,I1,Info).
2264 cleanup_post(external_function_call('ENUM',[TA]),T,I,A,T,[prob_annotation('ENUM')|I],single/process_ENUM) :-
2265 (debug_mode(on) -> format(' Processing ENUM (~w): ',[T]),translate:print_bexpr(TA),nl ; true),
2266 % TO DO: do not process in DEFINITION of ENUM in LibraryProB.def
2267 get_texpr_expr(TA,A).
2268 cleanup_post(external_function_call('FORCE',[TA]),T,I,external_function_call('FORCE',[TA2]),T,I,single/process_FORCE) :-
2269 mark_comprehension_set_with(TA,'FORCE',TA2), % could be sufficient on its own if TA is a comprehension set
2270 % but we keep the call to FORCE, in case TA is re-written or more complex
2271 (debug_mode(off) -> true
2272 ; format(' Processing FORCE: ',[]),translate:print_bexpr(TA2),nl).
2273 cleanup_post(comprehension_set(TIds,Body),T,I1,comprehension_set(TIds,Body2),T,I1,single/detect_lambda_result_auto) :-
2274 get_texpr_info(Body,BI),
2275 (TIds=[_,_,_|_] % check if at least three ids
2276 -> true % for example useful here: {x,y,v| x:INTEGER & y:INTEGER & #z.(z=x+1 & x = y*y & v=z*z & y:1..10)} it could be useful to detect x as DO_NOT_ENUMERATE; currently we only enable this analysis for four ids at least
2277 ; nonmember(prob_annotation('LAMBDA'),BI) % it is a lambda with one argument, no use to do analysis
2278 ),
2279 perform_do_not_enumerate_analysis(TIds,Body,'SET COMPREHENSION',I1,Body2).
2280 cleanup_post(comprehension_set(Ids1,E1),T,I,comprehension_set(Ids2,E2),T,I,multi/detect_lambda_result_user_ann) :-
2281 get_texpr_expr(DNE,external_pred_call('DO_NOT_ENUMERATE',[TID])),
2282 get_texpr_id(TID,ID),
2283 member_in_conjunction(DNE,E1),
2284 NewInfo = prob_annotation('DO_NOT_ENUMERATE'(ID)), % similar to lambda_result
2285 E1 = b(PRED,pred,I1),
2286 nonmember(NewInfo,I1),
2287 get_texpr_id(TID,ID),
2288 nth1(Pos,Ids1,TID1,Rest),
2289 add_texpr_infos(TID1,[NewInfo],TID2),
2290 nth1(Pos,Ids2,TID2,Rest),
2291 add_message(detect_lambda_result,'Annotating comprehension set identifier with DO_NOT_ENUMERATE: ',ID,I1),
2292 E2 = b(PRED,pred,[NewInfo|I1]).
2293 cleanup_post(record_field(b(rec(Fields),TR,IR),Field),T,I,FieldVal,T,I, single/remove_field_access) :-
2294 always_well_defined_or_disprover_mode(b(rec(Fields),TR,IR)),
2295 member(field(Field,TFieldVal),Fields),!,
2296 (debug_mode(off) -> true ; add_message(remove_field_access,'Remove static field access: ',Field,I)), % cf test 1294
2297 get_texpr_expr(TFieldVal,FieldVal).
2298 cleanup_post(sequence([S1,b(sequence(S2),subst,_)]),subst,I,sequence([S1|S2]),subst,I, single/flatten_sequence2) :-
2299 debug_println(9,flatten_sequence2). % do we need something for longer sequences?
2300 cleanup_post(sequence([b(sequence(S1),subst,_)|S2]),subst,I,sequence(NewSeq),subst,I, single/flatten_sequence1) :-
2301 append(S1,S2,NewSeq),
2302 % avoid maybe calling filter_useless_subst_in_sequence again
2303 debug_println(9,flatten_sequence1).
2304 cleanup_post(sequence(S1),subst,I,sequence(S2),subst,I, single/remove_useless_subst_in_seuence) :-
2305 get_preference(useless_code_elimination,true),
2306 filter_useless_subst_in_sequence(S1,Change,S2), debug_println(filter_sequence(9,Change)).
2307 cleanup_post(sequence(Statements),subst,I,Result,subst,I, single/sequence_to_multi_assign) :-
2308 % merge sequence of assignments if possible
2309 merge_assignments(Statements,Merge,New), Merge==merged,
2310 construct_sequence(New,Result).
2311 % nl,print('Merged: '),translate:print_subst(b(Result,subst,I)),nl,nl.
2312 cleanup_post(parallel(Statements),subst,I,Result,subst,I, single/parallel_to_multi_assign) :-
2313 % this merges multiple assignments into a single one: advantage: only one waitflag set up
2314 % should probably not be done in INITIALISATION
2315 % print(parallel(Statements)),nl,trace,
2316 extract_assignments(Statements,LHS,RHS,Rest,Nr), % print(extracted(Nr,LHS)),nl,
2317 Nr>1,!,
2318 (debug_mode(on) ->
2319 print('Parallel to Assignment: '), translate:print_subst(b(parallel(Statements),subst,I)),nl
2320 ; true),
2321 (Rest == [] -> Result = assign(LHS,RHS)
2322 ; Result = parallel([b(assign(LHS,RHS),subst,[])|Rest])).
2323 %translate:print_subst(b(Result,subst,I)),nl.
2324 cleanup_post(select([CHOICE|Rest]),subst,I0,S,subst,Info,single/remove_select) :-
2325 Rest = [], % SELECT can have multiple true branches
2326 CHOICE=b(select_when(TRUTH,Subst),subst,_),
2327 is_truth(TRUTH),!,
2328 debug_println(19,'Removing useless SELECT'),
2329 get_texpr_expr(Subst,S),get_texpr_info(Subst,I1),
2330 add_hint_message(remove_select,'Removing useless SELECT','',I1),
2331 add_important_info_from_super_expression(I0,I1,Info).
2332 cleanup_post(select([CHOICE|Rest],_ELSE),subst,OldInfo,Res,subst,I,single/remove_select_else) :-
2333 CHOICE=b(select_when(TRUTH,Subst),subst,_),
2334 is_truth(TRUTH),!,
2335 (Rest = [] % completely useless SELECT
2336 -> add_hint_message(remove_select_else,'Removing useless SELECT','',OldInfo),
2337 get_texpr_expr(Subst,Res),get_texpr_info(Subst,I1),
2338 add_important_info_from_super_expression(OldInfo,I1,I)
2339 ; add_hint_message(remove_select_else,'Removing useless SELECT ELSE branch','',OldInfo),
2340 Res = select([CHOICE|Rest]), I=OldInfo).
2341 cleanup_post(let_expression([],[],TExpr),Type,I0,Expr,Type,I,multi/remove_let_expression) :- !,
2342 % remove trivial let expressions without any introduced identifiers
2343 % this rule makes only sense in combination with the next rule which removes
2344 % simple let identifiers
2345 get_texpr_expr(TExpr,Expr),
2346 get_texpr_info(TExpr,I1),
2347 add_important_info_from_super_expression(I0,I1,I).
2348 cleanup_post(let([],Pred,TExpr),Type,I0,Expr,Type,I,multi/remove_let) :- is_truth(Pred),!,
2349 % remove trivial let expressions without any introduced identifiers
2350 % this rule makes only sense in combination with the next rule which removes
2351 % simple let identifiers
2352 get_texpr_expr(TExpr,Expr),
2353 get_texpr_info(TExpr,I1),
2354 add_important_info_from_super_expression(I0,I1,I).
2355 cleanup_post(let_expression(TIds,Exprs,Expr),Type,I,
2356 let_expression(NIds,NExprs,NExpr),Type,I,multi/remove_let_expression2) :-
2357 ? simplify_let(TIds,Exprs,Expr,NIds,NExprs,NExpr),!,
2358 %format('~n Simplified Let ~w --> ~w~n',[TIds,NIds]),
2359 true.
2360 cleanup_post(let_predicate(TIds,Exprs,Body),Type,I,
2361 let_predicate(NIds,NExprs,NBody),Type,I,multi/remove_let_predicate2) :-
2362 ? simplify_let(TIds,Exprs,Body,NIds,NExprs,NBody),!,
2363 %format('~n Simplified Let ~w --> ~w~n',[TIds,NIds]),
2364 (is_truth(NBody), NIds \=[]
2365 -> add_debug_message(b_ast_cleanup,'Useless LET predicate: ',Body,I) % not removed due to WD ?
2366 ; true). % if NIds = [] then we will remove it in cleanup_post_essential
2367 cleanup_post(let_predicate(TIds,Exprs,Body),Type,I,
2368 NewExpr,Type,NewI,single/useless_let_message_or_removal) :-
2369 is_truth(Body),
2370 nonmember(useless_let,I),
2371 get_texpr_ids(TIds,Ids),
2372 TE=b(let_predicate(TIds,Exprs,Body),Type,I),
2373 (always_well_defined_or_wd_improvements_allowed(TE)
2374 -> NewExpr = truth, delete(I,contains_wd_condition,NewI),
2375 (always_well_defined_or_disprover_mode(TE)
2376 -> true
2377 ; TIds=[TID1|_],
2378 add_message(b_ast_cleanup,'Removing useless existentially quantified variables: ',Ids,TID1)
2379 )
2380 ; NewExpr = let_predicate(TIds,Exprs,Body), NewI=[useless_let|I],
2381 not_generated_exists_paras(TIds),
2382 % otherwise this was generated programmatically, e.g., in get_operation_enabling_condition, see test 625
2383 (nonmember(allow_to_lift_exists,I) -> true % ditto, e.g., by create_outer_exists_for_dom_range, see test 1945
2384 ; get_preference(data_validation_mode,true)),
2385 add_message(b_ast_cleanup,'Useless existentially quantified variables: ',Ids,TIds)
2386 ).
2387 % was disabled because simplify_let_subst also replaced in RHS of assignments
2388 % but now we check that a LET/ANY variable cannot be assigned to statically
2389 % so it should be safe now to replace simple equalities:
2390 cleanup_post(let(Ids,Pred,Subst),Type,I,
2391 let(NIds,NPred,NSubst),Type,I,multi/remove_let_subst2) :-
2392 simplify_let_subst(Ids,Pred,Subst,NIds,NPred,NSubst),! ,
2393 true. % translate:print_subst(b(let(NIds,NPred,NSubst),subst,[])),nl.
2394
2395
2396 cleanup_post(forall([ID],LHS,RHS),pred,IOld,Res,pred,IOld,single/expand_forall_set_extension) :-
2397 % expand !x.(x:{a,b,...} => RHS) into conjunction
2398 % can be useful e.g. for KODKOD when we pick an element from a set of sets
2399 \+ preferences:has_default_value(use_solver_on_load), % prob, used to be only enabled in Kodkod mode
2400 % TO DO: enable always; but maybe check that set_extension can be computed (which eval_set_extension will do) to avoid duplicating checks (!y.(y:{v,w} => expensive_pred) + what if v=w
2401 nonmember(do_not_optimize_away,IOld),
2402 get_texpr_expr(LHS,member(ID2,Set)),
2403 same_texpr(ID,ID2),
2404 is_set_extension(Set,SList),
2405 get_texpr_id(ID,AID),
2406 debug_format(19,'Expanding forall ~w ',[AID]),
2407 findall(C, (member(SEL,SList),replace_id_by_expr(RHS,AID,SEL,C)),Conjuncts),
2408 conjunct_predicates_with_pos_info(Conjuncts,ExpandedForAll),
2409 (silent_mode(on) -> true ; print_bexpr(ExpandedForAll),nl),
2410 get_texpr_expr(ExpandedForAll,Res).
2411 cleanup_post(forall(Ids1,LHS,RHS),pred,IOld,
2412 forall(Ids,NewLHS,NewRHS),pred,INew,multi/merge_forall) :-
2413 is_truth(LHS),
2414 RHS = b(forall(Ids2,NewLHS,NewRHS),pred,_),
2415 %((member(b(identifier(_),Type,_),Ids1), is_infinite_ground_type(Type)) -> true), % could be useful for tests 1441, 1447 ??
2416 (disjoint_ids(Ids1,Ids2)
2417 -> append(Ids1,Ids2,Ids),
2418 % !x.(truth => !y.(P=>Q) <==> !(x,y).(P=>Q)
2419 (debug_mode(off) -> true ; format('Merging forall ~w: ',[Ids]), print_bexpr(NewLHS),nl),
2420 add_removed_typing_info(IOld,INew)
2421 ; \+ preferences:get_preference(disprover_mode,true),
2422 translate:translate_bexpression(b(forall(Ids1,LHS,RHS),pred,IOld),PS),
2423 add_warning(b_ast_cleanup,'Variable clash in nested universal quantification: ',PS,IOld),
2424 fail
2425 ).
2426 cleanup_post(forall(Ids,LHS,RHS),pred,IOld,
2427 implication(Outer,FORALL),pred,IOld,single/detect_global_preds_forall1) :-
2428 % !x.(P(x) & Q => R(x) <==> Q => !x.(P(x) => R(x))
2429 % TO DO: maybe we should not lift things like printf, ... ?
2430 bsyntaxtree:detect_global_predicates(Ids,LHS,Outer,Inner),
2431 (debug_mode(off) -> true ; format('Lifting predicate (lhs) of forall ~w: ',[Ids]), print_bexpr(Outer),nl),
2432 construct_inner_forall(Ids,Inner,RHS,IOld,FORALL).
2433 % TO DO: implement similar lifting rules for exists(Ids,P)
2434 cleanup_post(forall(Ids,LHS,RHS),pred,IOld,
2435 conjunct(Outer,FORALL),pred,IOld,single/detect_global_preds_forall2) :-
2436 is_truth(LHS),
2437 % !x.(truth => Q & R(x) <==> Q & !x.(truth => R(x))
2438 bsyntaxtree:detect_global_predicates(Ids,RHS,Outer,Inner),
2439 (debug_mode(off) -> true ; format('Lifting predicate (rhs &) of forall ~w: ',[Ids]), print_bexpr(Outer),nl),
2440 construct_inner_forall(Ids,LHS,Inner,IOld, FORALL). % print_bexpr(FORALL),nl,nl.
2441 cleanup_post(forall(Ids,LHS,RHS),pred,IOld,
2442 implication(Outer,FORALL),pred,IOld,single/detect_global_preds_forall3) :-
2443 is_truth(LHS),
2444 RHS = b(implication(RHS1,RHS2),pred,_),
2445 % !x.(truth => (Q & R(x) => S(x)) <==> Q => !x.(truth => R(x) => S(x))
2446 bsyntaxtree:detect_global_predicates(Ids,RHS1,Outer,Inner),
2447 create_implication(Inner,RHS2,NewRHS),
2448 (debug_mode(off) -> true ; format('Lifting predicate (rhs =>) of forall ~w: ',[Ids]), print_bexpr(Outer),nl),
2449 construct_inner_forall(Ids,LHS,NewRHS,IOld, FORALL).
2450 cleanup_post(forall([TID1,TID2|OTHER],LHS,RHS),pred,IOld,
2451 forall([TID1,TID2|OTHER],NewLHS,RHS),pred,[prob_symmetry(ID1,ID2)|IOld],single/symmetry_detection) :-
2452 % DETECT Symmetries such as !(x,y).(x /= y => x=TRUE or y=TRUE)
2453 % !(x2,y).(x2 /= y & x2:s & y:s => x2=aa or y=aa)
2454 % f:1..n --> 1..(n-1) & !(x,y).(x/=y &x:dom(f) & y:1..n => f(x) /= f(y)) & n=9 (runtime 1 sec -> 0.78 sec)
2455 get_texpr_type(TID1,T), get_texpr_type(TID2,T),
2456 \+ preferences:get_preference(use_solver_on_load,kodkod), % e.g., Kodkod cannot properly deal with LEQ_SYM_BREAK, treats it like truth
2457 preferences:get_preference(use_static_symmetry_detection,true),
2458 sym_break_supported_type(T), % LEQ_SYM_BREAK not yet fully functional with SET types; TO DO: fix
2459 get_texpr_id(TID1,ID1), get_texpr_id(TID2,ID2),
2460 nonmember(prob_symmetry(ID1,ID2),IOld),
2461 rename_bt(LHS,[rename(ID1,ID2),rename(ID2,ID1)],LHS2),
2462 same_norm_texpr(LHS,LHS2),
2463 rename_bt(RHS,[rename(ID1,ID2),rename(ID2,ID1)],RHS2),
2464 same_norm_texpr(RHS,RHS2),
2465 construct_sym_break(T,TID1,TID2,LHS,SYMBREAK),
2466 conjunct_predicates_with_pos_info(LHS,SYMBREAK,NewLHS),
2467 (debug_mode(off) -> true
2468 ; format('SYMMETRY BREAKING FORALL: !(~w,~w).(',[ID1,ID2]),print_bexpr(NewLHS),
2469 print(' => '), print_bexpr(RHS),print(')'),nl).
2470 cleanup_post(forall(AllIds,P,Rhs),pred,I,NewPred,pred,INew,multi/forall_splitting) :-
2471 AllIds = [_TID1|TRestIDs0], TRestIDs0 = [_|_],
2472 get_preference(use_clpfd_solver,true), % with CLPFD false: maybe more likely to reduce performance
2473 % NOTE: we could destroy symmetry reduction detection if TRestIDs = [TID2], but we run after symmetry detection
2474 % !(x,y,...).(x:SET & RestPred => Rhs) == !x.(x:SET => !(y,..).(RestPred => Rhs))
2475 conjunction_to_list(P,[MEM|RestPreds]),
2476 is_membership(MEM,LHS,Set),
2477 ? is_forall_membership_pattern_match(LHS,AllIds,PatMatchIds,TRestIDs),
2478 TRestIDs \= [],
2479 %same_id(TID1,TID,ID),
2480 \+ definitely_infinite(Set), % prevent !(x,y).(x:NATURAL & x<10 & y :1..x => x+y<20)
2481 %\+ known_set(Set), % rewriting makes sense if Set is not fully known and will be instantiated during solving
2482 % used to prevent known sets, but rewriting also useful for known sets (QueensWithEvents_ForallTest2b)
2483 get_sorted_ids(TRestIDs,RestIDs),
2484 not_occurs_in_predicate(RestIDs,Set),
2485 NewPred = forall(PatMatchIds,MEM,InnerForall),
2486 conjunct_predicates_with_pos_info(RestPreds,InnerForallLhs),
2487 construct_inner_forall(TRestIDs,InnerForallLhs,Rhs,I,InnerForall),
2488 !,
2489 delete(I,used_ids(_),I1), % should in principle still be ok at outer level; but just be sure
2490 add_removed_typing_info(I1,INew),
2491 (debug_mode(on) -> get_sorted_ids(PatMatchIds,PatIds),
2492 format('FORALL SPLITTING ~w (from ~w) for better propagation: ',[PatIds,RestIDs]),
2493 print_bexpr(b(NewPred,pred,INew)),nl
2494 ; true).
2495 cleanup_post(exists([TID],MemPred),pred,IOld,
2496 Res,pred,IOld, single/replace_exists_by_not_empty) :-
2497 % simplify #ID.(ID:E) <=> E /= {}
2498 % simplify #ID.(ID:E1 & ID:E2) <=> E1 /\ E2 /= {} , etc...
2499 % important e.g. for y:20..30000000000 & not(#x.(x:1..10 & x:8..y))
2500 ? is_valid_id_member_check(MemPred,TID,E),
2501 !,
2502 (definitely_not_empty_set(E) -> Res= truth
2503 ; get_texpr_type(E,Type), EmptySet=b(empty_set,Type,[]),
2504 Res = not_equal(E,EmptySet)),
2505 (debug_mode(off) -> true
2506 ; get_texpr_id(TID,ID),
2507 format('Removing existential quantifier: ~w~n',[ID]),
2508 print_bexpr(b(Res,pred,IOld)),nl).
2509 cleanup_post(exists([TID],b(NotMemPred,_,_)),pred,IOld,
2510 not_equal(E,TypeExpr),pred,IOld, single/replace_exists_by_not_full) :-
2511 % simplify #ID.(ID/:E) <=> E /= FullType
2512 % important e.g. for y:7..30000000000 & not(#x.(x /: 1..y))
2513 is_not_member(NotMemPred,MID,E),
2514 same_id(TID,MID,SID),
2515 % + check that MID does not occur in E
2516 \+ occurs_in_expr(SID,E),
2517 get_texpr_type(E,SType),
2518 is_set_type(SType,Type),
2519 create_maximal_type_set(Type,TypeExpr), % Note: no longer introduces identifiers but value(.) results
2520 !,
2521 (debug_mode(off) -> true
2522 ; format('Removing existential quantifier: ~w~n',[SID]),
2523 print_bexpr(b(not_equal(E,TypeExpr),pred,IOld)),nl).
2524 cleanup_post(exists([TID],b(Pred,_,_)),pred,IOld,
2525 truth,pred,IOld, single/replace_exists_by_truth) :-
2526 b_interpreter_check:arithmetic_op(Pred,_Op,X,Y),
2527 ( (same_id(TID,X,SID), \+ occurs_in_expr(SID,Y), always_well_defined_or_disprover_mode(Y)) ;
2528 (same_id(TID,Y,SID), \+ occurs_in_expr(SID,X), always_well_defined_or_disprover_mode(X))
2529 ),
2530 !, % we have a formula of the form #SID.(SID > Expr); provided Expr is well-defined, this is always true
2531 (debug_mode(off) -> true
2532 ; format('Removing existential quantifier: ~w~n',[SID]),
2533 print_bexpr(b(Pred,pred,[])),nl).
2534 cleanup_post(exists([TID1,TID2|OTHER],RHS),pred,IOld,
2535 exists([TID1,TID2|OTHER],NewRHS),pred,[prob_symmetry(ID1,ID2)|IOld],single/symmetry_detection) :-
2536 % DETECT Symmetries such as #(x,y).(x /= y & (x=TRUE or y=TRUE))
2537 % #(x2,y).(x2 /= y & x2:s & y:s & x2=aa or y=aa)
2538 get_texpr_type(TID1,T), get_texpr_type(TID2,T),
2539 \+ preferences:get_preference(use_solver_on_load,kodkod),
2540 preferences:get_preference(use_static_symmetry_detection,true),
2541 sym_break_supported_type(T), % LESS not yet fully functional with SET types; TO DO: fix
2542 get_texpr_id(TID1,ID1), get_texpr_id(TID2,ID2),
2543 nonmember(prob_symmetry(ID1,ID2),IOld),
2544 ? \+(contains_equality(TID1,TID2,RHS)), % IDs are already equal; no use in sym breaking
2545 rename_bt(RHS,[rename(ID1,ID2),rename(ID2,ID1)],RHS2),
2546 same_norm_texpr(RHS,RHS2),
2547 construct_sym_break(T,TID1,TID2,RHS,SYMBREAK),
2548 conjunct_predicates_with_pos_info(RHS,SYMBREAK,NewRHS),
2549 (debug_mode(off) -> true
2550 ; format('SYMMETRY BREAKING EXISTS: #(~w,~w).(',[ID1,ID2]),print_bexpr(NewRHS),print(')'),nl).
2551 cleanup_post(Expr,T,I1,NewExpr,T,I1,single/detect_lambda_result_quant_auto) :-
2552 construct_for_find_do_not_enumerate(Expr,KIND,TIds,Body,NewExpr,NewBody),
2553 perform_do_not_enumerate_analysis(TIds,Body,KIND,I1,NewBody).
2554 % sort args of commutative operators by term size
2555 cleanup_post(Expr, Type, I, NExpr, Type, NI, single/normalize_commutative_args) :-
2556 preferences:get_preference(normalize_ast_sort_commutative, true),
2557 sort_commutative_args(Expr, I, NExpr, NI).
2558 %,(NExpr=Expr -> true ; print('COMMUTE: '), translate:print_bexpr(b(Expr,Type,I)),nl, print(' TO: '), translate:print_bexpr(b(NExpr,Type,NI)),nl).
2559 %% COMMENT IN NEXT LINE TO CHECK validity of AST per NODE (helps find bugs)
2560 %%cleanup_post(Expr,pred,I,Expr,pred,I,single/checked) :- check_ast(true,b(Expr,pred,I)),fail.
2561 %%
2562 cleanup_post(Expr, pred, I, Expr, pred, [DI|I], single/detect_prob_ignore) :-
2563 ? get_info_labels(I,Labels), member(Label,Labels),
2564 is_prob_ignore_label(Label),
2565 !,
2566 DI=description('prob-ignore'), % detected by info_has_ignore_pragma/1, predicate_has_ignore_pragma/1
2567 nonmember(DI,I),
2568 (get_preference(use_ignore_pragmas,true)
2569 -> add_message(detect_prob_ignore,'Detected prob-ignore label: ',Label,I)
2570 ; add_message(detect_prob_ignore,'Detected prob-ignore label (but USE_IGNORE_PRAGMAS preference is FALSE!): ',Label,I)
2571 ).
2572
2573 is_prob_ignore_label(Label) :-
2574 atom_codes(Label,Cs),
2575 IGNORE = [112,114,111,98,DASH,105,103,110,111,114,101|_],
2576 % used to be: append("prob-ignore",_,IGNORE), but Rodin editor sometimes puts Dash 8722 rather than 45 in label
2577 suffix(Cs,IGNORE), % accept prob-ignore somewhere in the label
2578 member(DASH,[45, 8722, 95, 46, 32, 126, 61, 43, 35]). % "--_. ~=+#", 8722 is 0x2212 is Unicode minus
2579 % used to call reverse and match "erongi-borp"
2580
2581 cleanup_post_function(Override,X,_Type,I0,Res,Info,multi/function_override) :-
2582 preferences:get_preference(disprover_mode,true), % only applied in Disprover mode as it can remove WD problem; TO DO make it also applicable in normal mode
2583 get_texpr_expr(Override,overwrite(_F,SEXt)),
2584 SEXt = b(set_extension(LIST),_,_),
2585 member(b(couple(From,To),_,_),LIST),
2586 same_texpr(From,X),
2587 % ( F <+ { ... X|->To ...}) (X) ==> To
2588 %print_bexpr(b(function(Override,X),Type,_I)), print(' ==> '), print_bexpr(To),nl,
2589 !,
2590 get_texpr_expr(To,Res),
2591 get_texpr_info(To,I1),
2592 add_important_info_from_super_expression(I0,I1,Info).
2593 cleanup_post_function(Override,X,Type,Info,Res,Info,multi/function_override_ifte) :-
2594 preferences:get_preference(disprover_mode,true), % only applied in Disprover mode as it can remove WD
2595 % TO DO: should we do this generally? or simply deal with overwrite symbolically always?
2596 get_texpr_expr(Override,overwrite(F,SEXt)),
2597 SEXt = b(set_extension([Couple]),_,_),
2598 Couple = b(couple(From,To),_,_),
2599 % (f <+ {A|->B}) (x) -> if_then_else(x=A,B,f(x)) ; avoids having to explicitly compute f<+{A|->B}
2600 Res = if_then_else(EqXFrom,To,FX),
2601 safe_create_texpr(equal(X,From),pred,EqXFrom),
2602 safe_create_function_call(F,X,Type,Info,FX).
2603 cleanup_post_function(SEXt,ARG,_Type,I0,Res,Info,multi/function_set_extension) :-
2604 SEXt = b(set_extension(LIST),_,ListInfos), % TO DO: also support b(value(avl_set(A)),_,_)
2605 eval_set_extension_element(ARG,Value,not_available),
2606 ? select(b(couple(LHS,RHS),_,_),LIST,REST),
2607 (member(contains_wd_condition,ListInfos) % then we could remove WD problem, e.g., r = {1|->2, 2|-> 1/0}(1)
2608 -> (preferences:preference(find_abort_values,false) ;
2609 preferences:get_preference(disprover_mode,true))
2610 ; true),
2611 eval_set_extension_element(LHS,Value,not_available), %nl,print(found(RHS)),nl,
2612 % WE NEED TO Check that all LHS can be compared against ARG
2613 \+ member((b(couple(LHS2,_),_,_),REST),eval_set_extension_element(LHS2,Value,not_available)), % no other potential match
2614 \+ member((b(couple(LHS3,_),_,_),LIST), \+ eval_set_extension_element(LHS3,_,not_available)), % all left-hand-sides can be evaluated
2615 !, get_texpr_expr(RHS,Res), get_texpr_info(RHS,I1),
2616 add_important_info_from_super_expression(I0,I1,Info).
2617 % Detect if_then_else in format as printed by pp_expr2(if_then_else( ....)...) or as generated by B2TLA:
2618 cleanup_post_function(IFT,DUMMYARG,_Type,Info,if_then_else(IFPRED,THEN,ELSE),Info,multi/function_if_then_else) :-
2619 is_if_then_else(IFT,post,DUMMYARG,IFPRED,THEN,ELSE),
2620 (debug_mode(off) -> true
2621 ; print('% Recognised if-then-else expression: IF '), print_bexpr(IFPRED),
2622 print(' THEN '),print_bexpr(THEN), print(' ELSE '),print_bexpr(ELSE),nl
2623 ).
2624 cleanup_post_function(Composition,X,Type,Info,NewExpr,Info,multi/function_composition) :-
2625 (data_validation_mode ;
2626 get_preference(convert_comprehension_sets_into_closures,true)
2627 % there it can make a big difference in particular since relational composition (rel_composition_wf -> rel_compose_with_inf_fun case) was not fully symbolic; see Systerel data validation examples
2628 ),
2629 % (F;G)(X) --> G(F(X)) (F;G;H)(X) --> H(G(F(X))) ...
2630 peel_rel_composition(Composition,Type,Info,X,Result,Level),
2631 Level>0,
2632 Result = b(NewExpr,_,_),
2633 %check_ast(Result), % <---
2634 (debug_mode(off) -> true
2635 ; format('Function application of COMPOSITION (Nesting: ~w) translated to: ',[Level]),print_bexpr(Result),nl).
2636 cleanup_post_function(Fun,Arg,_Type,Info,function(Fun,Arg),NewInfo,single/function_inversion_annotation) :-
2637 data_validation_mode, % TODO: activate in general
2638 limited_propagation(Arg),
2639 NewInfo = [prob_annotation('INVERSION_PENALTY')|Info],
2640 (debug_mode(off) -> true
2641 ; add_message(ast_cleanup,'Function application not suited for propagation from result to argument: ',Arg,Info)).
2642
2643 % statically detect whether there is potential for propagating result values onto these expressions
2644 limited_propagation(b(Expr,_,_)) :- limited_prop_aux(Expr).
2645 limited_prop_aux(function(_,_)). % we need to propagate through at least one more function call
2646 limited_prop_aux(composition(_,_)). % argument is a function, composed of two other functions
2647 limited_prop_aux(iteration(_,_)).
2648 limited_prop_aux(record_field(Rec,_)) :- limited_propagation(Rec). % we only know part of the record; TODO: add a penalty even if Rec allows propagation
2649 %limited_prop_aux(external_function_call(_,_)). % MU probably ok, for other funs it depends
2650 limited_prop_aux(couple(A,B)) :-
2651 (limited_propagation(A)
2652 -> % back propagation on A not useful,
2653 (limited_propagation(B) -> true % back propagation on both arguments is not useful
2654 ; is_constant(B) % B is a constant and could only filter (if TRY_FIND_ABORT / find_abort_values false)
2655 )
2656 ; is_constant(A), limited_propagation(B) % what if both are a constant ?
2657 ).
2658 limited_prop_aux(minus(A,B)) :- (limited_propagation(A) -> true ; limited_propagation(B)).
2659 limited_prop_aux(add(A,B)) :- (limited_propagation(A) -> true ; limited_propagation(B)).
2660 limited_prop_aux(div(A,B)) :- (limited_propagation(A) -> true ; limited_propagation(B)).
2661 limited_prop_aux(multiplication(A,B)) :- (limited_propagation(A) -> true ; limited_propagation(B)).
2662 limited_prop_aux(let_expression(_,_,C)) :- limited_propagation(C).
2663 % TODO: detect more cases where propagating from result of function application to argument is of limited value
2664 % sequence operators: insert_tail, first, last, ..., sequence_extension (cf test 2387)
2665
2666 is_constant(b(C,_,_)) :- is_const_aux(C).
2667 is_const_aux(boolean_false).
2668 is_const_aux(boolean_true).
2669 is_const_aux(couple(A,B)) :- is_constant(A),is_constant(B).
2670 is_const_aux(empty_set).
2671 is_const_aux(empty_sequence).
2672 is_const_aux(integer(_)).
2673 is_const_aux(real(_)).
2674 is_const_aux(string(_)).
2675 is_const_aux(value(V)) :- nonvar(V), is_const_value(V).
2676 % not covered yet: value(_) and record(_)
2677 is_const_value(pred_true).
2678 is_const_value(pred_false).
2679 is_const_value(string(S)) :- ground(S).
2680 is_const_value(int(S)) :- integer(S).
2681 is_const_value(fd(V,T)) :- ground(V), ground(T).
2682 is_const_value(term(floating(F))) :- number(F).
2683 is_const_value((A,B)) :- nonvar(A), nonvar(B), is_const_value(A),is_const_value(B).
2684
2685
2686 :- use_module(closures,[is_recursive_closure/3]).
2687 % detect comprehension set or closure values that can be inlined in member/not_member checks
2688 is_comprehension_set(comprehension_set(TypedIds,Body),TypedIds,Body).
2689 is_comprehension_set(value(Closure),TypedIds,Body) :- nonvar(Closure),
2690 Closure = closure(P,T,Body),
2691 \+ is_recursive_closure(P,T,Body), % otherwise inlining may expose unbound rec. ID, see test 554
2692 \+ custom_explicit_sets:is_interval_closure_or_integerset(Closure,_,_), % this is already a value
2693 % TO DO: also detect simple member closures like seq(0..1)
2694 create_typed_ids(P,T,TypedIds).
2695
2696 cleanup_comprehension_set([TID],b(member(LHS,TSet),pred,_),Type,I,Set,I2,multi/remove_useless_comprehension_set) :-
2697 get_texpr_id(TID,ID),
2698 get_texpr_id(LHS,ID),
2699 TSet=b(Set,Type,I2),
2700 (member(prob_annotation('SYMBOLIC'),I)
2701 -> % maybe this set comprehension was created with the purpose of adding the symbolic annotation
2702 % e.g., Set = closure(SET) stemming from /*@symbolic*/ {x|x:closure1(SET)}
2703 fail % TODO: see if we can propagate the symbolic annotation to TSet
2704 ; true),
2705 % {ID|ID:Set} ==> Set
2706 not_occurs_in_expr(ID,TSet),
2707 !,
2708 add_hint_message(remove_useless_comprehension_set,'Removing useless comprehension set over: ',ID,I).
2709 cleanup_comprehension_set([TID],Body,_Type,I,NewExpr,NewInfo,single/detect_seq_comprehension_set) :-
2710 get_texpr_id(TID,ID),
2711 % {ID | #LenID. (ID : 1..LenID --> Set)} ==> seq(Set)
2712 % {ID | #LenID. (ID : 1..LenID >-> Set)} ==> iseq(Set)
2713 get_texpr_expr(Body,exists([TLen],TIBody)),
2714 get_texpr_id(TLen,LenID), LenID \= ID,
2715 get_texpr_expr(TIBody,IBody),
2716 get_member(IBody,LenID,ISet,TID2,FUNCTION),
2717 get_texpr_id(TID2,ID),
2718 get_seq_fun_aux(FUNCTION,ISet,Interval,SeqTypeSet),
2719 get_texpr_expr(Interval,interval(ONE,TLen2)),
2720 get_texpr_id_with_offset(TLen2,LenID,Offset), % LenID or something like LenID-1
2721 get_integer(ONE,StartingIndex),!,
2722 (StartingIndex = 1, Offset=0 % TODO: check if a negative offset is always ok for all Seq Types
2723 -> add_hint_message(detect_seq_comprehension_set,'Detecting sequence operator: ',ID,I),
2724 NewExpr = SeqTypeSet, NewInfo=I
2725 ; % we have something sequence like but with indexes not starting at 1, maybe at 0
2726 % e.g. {ID | #LenID. (ID : 0..LenID-1 --> Set)}; happens in Soton/UML-B drone model
2727 add_hint_message(detect_seq_comprehension_set,'Marking sequence like operator as symbolic, indexes are not starting at 1: ',ID,I),
2728 NewExpr = comprehension_set([TID],Body),
2729 %add_texpr_infos(Body,[prob_annotation('SYMBOLIC')],Body2),
2730 add_info_if_new(I,prob_annotation('SYMBOLIC'),NewInfo)
2731 ).
2732 % {RANGE_LAMBDA__|#x.(x : dom(F) & RANGE_LAMBDA__ = F(x))} --> ran(F) // generated by TLC4B -> TLA2B
2733 % in Event-B notation: {x·x : dom(p)|p(x)}
2734 % TO DO: detect {R|R:INTEGER & #D.(D|->R:F)} as ran(F)
2735 cleanup_comprehension_set([TID],Body, _Type, I, range(Func), I, multi/detect_tla_range_comprehension_set) :-
2736 Body = b(exists([DomTID],EBody),pred,_),
2737 EBody = b(conjunct(LHS,RHS),pred,_),
2738 is_membership(LHS,DomTID1,b(domain(Func),_,_)),
2739 same_id(DomTID,DomTID1,_),
2740 is_equality(RHS,TID1,FunCall),
2741 FunCall = b(function(Func1,DomTID2),_,_),
2742 same_id(TID,TID1,_),
2743 same_id(DomTID,DomTID2,_),
2744 same_texpr(Func,Func1),
2745 % Note: we know arg is in domain; only problem is if Func is relation
2746 (animation_minor_mode(tla) -> true % we assume optimistically that Func is indeed a function
2747 ; always_well_defined_or_disprover_mode(FunCall) % we could just check if Func is a function
2748 ),
2749 add_hint_message(detect_tla_range_comprehension_set,'Detected range set-comprehension: ',Func,I).
2750 cleanup_comprehension_set([TID],Pred,Type,I,
2751 struct(b(rec(NewFieldSets),record(FieldTypes),I)),
2752 I,single/simplify_record) :-
2753 % {r|r'a : 1..1000 & r'b : 1..100 & r:struct(a:INTEGER,b:NAT,c:BOOL)} --> struct(a:1..1000,b:1..100,c:BOOL)
2754 % these kind of set comprehensions are generated by ProZ, see ROZ example model.tex test 1858
2755 % TO DO: maybe generalise this optimisation: currently it only works if all predicates can be assimilated into struct expression
2756 TID = b(identifier(ID),record(FieldTypes),_),
2757 conjunction_to_list(Pred,PL),
2758 l_update_record_field_membership(PL,ID,[],FieldSetsOut),
2759 maplist(construct_field_sets(FieldSetsOut),FieldTypes,NewFieldSets),
2760 (debug_mode(off) -> true
2761 ; print('Detected Record set comprehension: '),
2762 print_bexpr(b(struct(b(rec(NewFieldSets),record(FieldTypes),I)),Type,I)),nl).
2763 cleanup_comprehension_set(Ids1,E,_Type,I,comprehension_set(Ids2,E2),I,single/detect_lambda) :-
2764 preferences:get_preference(detect_lambdas,true),
2765 % used to lead to *** Enumerating lambda result warnings for test 1162, not anymore
2766 % does not yet detect: f = {x,y|x:NATURAL & (x> 1 &y=x+2)} & res = f[3..4]
2767 E = b(conjunct(LHS,Equality),pred,Info),
2768 nonmember(prob_annotation('LAMBDA'),Info), % not already processed
2769 ? identifier_equality(Equality,ID,TID1,Expr1),
2770 last(Ids1,TID), get_texpr_id(TID,ID),
2771 not_occurs_in_expr(ID,Expr1),
2772 not_occurs_in_predicate([ID],LHS),
2773 !,
2774 get_texpr_info(Equality,EqInfo),
2775 get_unique_id_inside('_lambda_result_',LHS,Expr1,ResultId), % currently the info field lambda_result is not enough: several parts of the ProB kernel match on the identifier name '_lambda_result_'
2776 TID1 = b(identifier(_),Type1,Info1),
2777 add_texpr_infos(b(identifier(ResultId),Type1,Info1),[lambda_result(ResultId),lambda_result_id_was(ID)],TID2),
2778 Equality2 = b(equal(TID2,Expr1),pred,[prob_annotation('LAMBDA-EQUALITY')|EqInfo]),
2779 E2 = b(conjunct(LHS,Equality2),pred,[prob_annotation('LAMBDA')|Info]),
2780 append(Ids0,[_],Ids1),
2781 append(Ids0,[TID2],Ids2),
2782 % code exists which is simpler, but has disadvantage of losing position info for equality
2783 (debug_mode(off) -> true ; format('Lambda using ~w detected: ',[ID]),print_bexpr(E2),nl).
2784 cleanup_comprehension_set(Ids,Body,_Type,I,NewExpr,I,single/infer_seq_types) :-
2785 infer_seq_types_for_tids(Ids,Body,Ids2), %write(cs(Ids2)),nl,
2786 NewExpr = comprehension_set(Ids2,Body).
2787
2788 % extract membership for ID and detect optional restrictions for Length identfier LenID
2789 get_member(conjunct(TA,TB),LenID,ISet,TID2,FUNCTION) :-
2790 get_texpr_expr(TA,A), % detect LenId : NATURAL(1)
2791 get_mem_of_integerset(A,LenID,ISet), % TO DO: detect LenID>0, or LenID /=0
2792 get_texpr_expr(TB,B),
2793 get_member1(B,TID2,FUNCTION).
2794 get_member(IBody,_,'NATURAL',TID2,F) :- get_member1(IBody,TID2,F).
2795 get_member1(member(TID2,b(FUNCTION,_,_)),TID2,FUNCTION).
2796
2797 get_mem_of_integerset(member(TID2,b(SET,_,_)),LenID,ISet) :- !,
2798 get_texpr_id(TID2,LenID),
2799 is_integer_set(SET,ISet).
2800 get_mem_of_integerset(Pred,LenID,ISet) :-
2801 is_integer_set_constraint_pred(Pred,LenID,ISet).
2802
2803
2804 get_texpr_id_with_offset(Expr,ID,0) :- get_texpr_id(Expr,ID).
2805 get_texpr_id_with_offset(b(add(TID,Expr),integer,_),ID,Offset) :-
2806 get_texpr_id(TID,ID), get_integer(Expr,Offset).
2807 get_texpr_id_with_offset(b(minus(TID,Expr),integer,_),ID,NegOffset) :-
2808 get_texpr_id(TID,ID), get_integer(Expr,Offset), NegOffset is -Offset.
2809
2810 get_seq_fun_aux(total_function(Interval,TargetSet),'NATURAL',Interval,seq(TargetSet)).
2811 get_seq_fun_aux(total_function(Interval,TargetSet),'NATURAL1',Interval,seq1(TargetSet)).
2812 get_seq_fun_aux(total_injection(Interval,TargetSet),'NATURAL',Interval,iseq(TargetSet)).
2813 get_seq_fun_aux(total_injection(Interval,TargetSet),'NATURAL1',Interval,iseq1(TargetSet)).
2814 get_seq_fun_aux(total_bijection(Interval,TargetSet),'NATURAL',Interval,perm(TargetSet)).
2815 get_seq_fun_aux(total_bijection(Interval,TargetSet),'NATURAL1',Interval,perm(TargetSet)) :-
2816 definitely_not_empty_set(TargetSet).
2817
2818 % Detect TLA real division defined by Div_1(a, b) == CHOOSE({m|m:REAL & m*b=a})
2819 is_tla_real_division(external_function_call('CHOOSE',[SETCOMPR]),TA,TB) :-
2820 get_texpr_expr(SETCOMPR,comprehension_set([TID1],BODY)),
2821 get_texpr_id(TID1,ID),
2822 is_equality( BODY, MUL, TA),
2823 get_texpr_expr(MUL,multiplication_real(TID2,TB)),
2824 get_texpr_id(TID2,ID).
2825
2826 % --------------------------------
2827
2828 % check if Expression can be used in b_interpreter for optimised forall treatment in !(AllIds).(Expr:SET => RHS)
2829 % see can_be_used_for_unification in b_interpreter
2830 is_forall_membership_pattern_match(TID,AllIds,[TID],RestIds) :-
2831 get_texpr_id(TID,ID),!,
2832 ? select(TID2,AllIds,RestIds), def_get_texpr_id(TID2,ID).
2833 is_forall_membership_pattern_match(b(Expr,_,_),AllIds,PatMatchIds,RestIds) :-
2834 ? is_forall_membership_pattern_match2(Expr,AllIds,PatMatchIds,RestIds).
2835 is_forall_membership_pattern_match2(integer(_),AllIds,[],AllIds).
2836 is_forall_membership_pattern_match2(string(_),AllIds,[],AllIds).
2837 is_forall_membership_pattern_match2(real(_),AllIds,[],AllIds).
2838 is_forall_membership_pattern_match2(boolean_true,AllIds,[],AllIds).
2839 is_forall_membership_pattern_match2(boolean_false,AllIds,[],AllIds).
2840 is_forall_membership_pattern_match2(value(V),AllIds,[],AllIds) :- nonvar(V), is_const_value(V).
2841 is_forall_membership_pattern_match2(couple(TA,TB),AllIds,PatMatchIds,RestIds) :-
2842 ? is_forall_membership_pattern_match(TA,AllIds,Pat1,Rest1),
2843 ? is_forall_membership_pattern_match(TB,Rest1,Pat2,RestIds),
2844 append(Pat1,Pat2,PatMatchIds).
2845 % TODO: enumerate_set elements, freevals (but then they must be supported in can_be_used_for_unification)
2846
2847 % --------------------------------
2848
2849 construct_for_find_do_not_enumerate(exists(TIds,Pred),'EXISTS',TIds,Pred,exists(TIds,Pred2),Pred2).
2850 construct_for_find_do_not_enumerate(any(TIds,Pred,Body),'ANY',TIds,Pred,any(TIds,Pred2,Body),Pred2) :-
2851 data_validation_mode. % as we do not examine Body of the Any, we do not know if there are additional constraints on TIds in the body
2852 % tests where ANYs would be annotated 471, 565, 808, 1196, 1489, 1850
2853
2854 % try and perform analysis for a body of either exists, any or set_comprehension
2855 perform_do_not_enumerate_analysis(TIds,Body,KIND,Span,NewBody) :-
2856 get_preference(perform_enumeration_order_analysis,true),
2857 get_texpr_info(Body,BI),
2858 nonmember(prob_annotation('DO_NOT_ENUMERATE'(_)),BI), % analysis was already performed or manually annotated
2859 find_do_not_enumerate_variables(TIds,Body,SortedVs,DelayVsInOrder),
2860 !,
2861 findall(prob_annotation('DELAY_ENUMERATION'(PosNr,DNID)),nth1(PosNr,DelayVsInOrder,DNID),I1),
2862 (SortedVs = []
2863 -> NewInfos = [prob_annotation('DO_NOT_ENUMERATE'('$$NONE$$'))|I1] % dummy marking to avoid running analysis again
2864 ; findall(prob_annotation('DO_NOT_ENUMERATE'(DNID)),member(DNID,SortedVs),NewInfos,I1)
2865 ),
2866 (debug_mode(off) -> true
2867 ; I1=[], SortedVs=[] -> true
2868 ; ajoin(['Annotating ',KIND,' identifiers with DO_NOT_ENUMERATE: '],Msg),
2869 %write(SortedVs),nl,
2870 add_message(detect_lambda_result_auto,Msg,SortedVs:DelayVsInOrder,Span)
2871 ),
2872 add_texpr_infos(Body,NewInfos,NewBody).
2873 perform_do_not_enumerate_analysis(_,Body,_,_,Body).
2874
2875 :- public check_do_not_enum_result/2.
2876 % check if there is a difference with stored result in info field if it exists
2877 check_do_not_enum_result(NewInfos,BInfo) :- member(prob_annotation('DO_NOT_ENUMERATE'(_)),BInfo),!,
2878 findall(DNID,
2879 (member(prob_annotation('DO_NOT_ENUMERATE'(DNID)),BInfo), DNID \= '$$NONE$$'), OldIds),
2880 findall(DNID,
2881 (member(prob_annotation('DO_NOT_ENUMERATE'(DNID)),NewInfos), DNID \= '$$NONE$$'), NewIds),
2882 (OldIds = NewIds
2883 -> format('Same DO_NOT_ENUMERATE result: ~w~n',[OldIds])
2884 ; format('Difference in DO_NOT_ENUMERATE result:~nOLD: ~w~nNEW: ~w~n',[OldIds,NewIds])
2885 ).
2886 check_do_not_enum_result(_,_).
2887
2888 % --------------------
2889
2890 % we only sort at the top-level, as cleanup_post will work its way bottom-up:
2891 sort_commutative_args(Expr, I, Sorted, NI) :- nonvar(Expr),
2892 functor(Expr, Functor, 2),
2893 is_commutative(Functor),
2894 !,
2895 arg(1, Expr, Lhs),
2896 arg(2, Expr, Rhs),
2897 remove_all_infos_and_ground(Lhs,CNLhs), % remove infos for comparison
2898 remove_all_infos_and_ground(Rhs,CNRhs),
2899 get_texpr_expr(CNLhs,N1),
2900 get_texpr_expr(CNRhs,N2),
2901 ( N1 @> N2
2902 -> Sorted =.. [Functor,Rhs,Lhs],
2903 NI = [was(Expr)|I]
2904 ; Sorted =.. [Functor,Lhs,Rhs],
2905 NI = I
2906 ).
2907
2908
2909 is_commutative(conjunct).
2910 is_commutative(disjunct).
2911 is_commutative(equivalence).
2912 is_commutative(equal).
2913 is_commutative(not_equal).
2914 is_commutative(add).
2915 is_commutative(multiplication).
2916 is_commutative(union).
2917 is_commutative(intersection).
2918
2919 % --------------------
2920
2921 factor_disjunct(CEquality1,CEquality2,IOld,New,INew) :-
2922 % (x=2 & y=3) or (x=2 & y=4) -> x=2 & (y=3 or y=4) to improve constraint propagation
2923 Blacklist=[],
2924 conjunction_to_list(CEquality1,Preds1),
2925 conjunction_to_list(CEquality2,Preds2),
2926 ? select_equality(TId1,Preds1,Blacklist,TEqual,Expr1,RestPreds1,_,check_well_definedness), % also allow other preds, use safe_select(check_well_definedness,TEqual,Preds,Rest),
2927 get_texpr_id(TId1,Id),
2928 get_texpr_id(TId2,Id),
2929 ? select_equality(TId2,Preds2,Blacklist,_,Expr2,RestPreds2,_,check_well_definedness),
2930 same_texpr(Expr1,Expr2),
2931 conjunct_predicates_with_pos_info(RestPreds1,P1),
2932 conjunct_predicates_with_pos_info(RestPreds2,P2),
2933 % TO DO: do not recursively start from scratch in P1, one should start from the right of CEquality1:
2934 (fail, % disable recursive looking
2935 factor_disjunct(P1,P2,IOld,P12,INew) -> NewDisj = b(P12,pred,INew)
2936 ; disjunct_predicates_with_pos_info(P1,P2,NewDisj)),
2937 conjunct_predicates_with_pos_info(TEqual,NewDisj,NewPred),
2938 NewPred=b(New,pred,I2),
2939 include_important_info_from_removed_pred(IOld,I2,INew).
2940
2941 % ------------------------------------------
2942
2943 gen_rename(TID1,TID2,rename(ID1,ID2)) :- def_get_texpr_id(TID1,ID1), def_get_texpr_id(TID2,ID2).
2944
2945 is_not_member(not_member(LHS,RHS),LHS,RHS).
2946 is_not_member(negation(b(member(LHS,RHS),pred,_)),LHS,RHS).
2947
2948
2949 :- use_module(typing_tools,[create_maximal_type_set/2]).
2950 is_valid_id_member_check(b(member(MID,E),_,_),ID,E) :- same_id(ID,MID,SID),
2951 % + check that MID does not occur in E
2952 \+ occurs_in_expr(SID,E).
2953 is_valid_id_member_check(b(truth,_,_),ID,TypeExpr) :-
2954 get_texpr_type(ID,SType),
2955 create_maximal_type_set(SType,TypeExpr). % Note: this no longer introduces identifiers, which could clash
2956 is_valid_id_member_check(b(conjunct(A,B),_,_),ID,Res) :-
2957 ? is_valid_id_member_check(A,ID,EA),
2958 ? is_valid_id_member_check(B,ID,EB),
2959 get_texpr_type(EA,Type),
2960 safe_create_texpr(intersection(EA,EB),Type,Res).
2961
2962 contains_equality(TID1,TID2,RHS) :-
2963 ? b_interpreter:member_conjunct(b(equal(A,B),pred,_),RHS,_),
2964 (same_id(A,TID1,_),same_id(B,TID2,_) ; same_id(A,TID2,_),same_id(B,TID1,_)).
2965
2966 simplify_let_subst(Ids,Pred,Subst,NIds,RestPred,NewSubst) :-
2967 % remove let identifiers whose definitions are very simple, i.e. identifiers
2968 Eq = b(equal(TypID,TExpr),pred,_),
2969 can_be_optimized_away(TypID),
2970 get_texpr_id(TypID,ToReplace),
2971 b_interpreter:member_conjunct(Eq,Pred,RestPred),
2972 is_simple_expression(TExpr),
2973 nth0(_N,Ids,TI,NIds),get_texpr_id(TI,ToReplace),
2974 !,
2975 % Intitially there was an issue as we may also replace in the LHS of assignments
2976 % see e.g. TestLet = LET cnt BE cnt=1 IN IF cnt=0 THEN ABORT ELSE cnt :: {0,1} END END; in SubstitutionLaws
2977 % However, now the static type checker rejects those assignments
2978 replace_id_by_expr(Subst,ToReplace,TExpr,NSubst),
2979 % TO DO: it seems like cleanup rules are not applied on NSubst, e.g., function for set_extension rules
2980 debug_println(9,replaced_let_subst_id(ToReplace)),
2981 (Subst==NSubst -> NewSubst=NSubst ; clean_up(NSubst,[],NewSubst)).
2982
2983 can_be_optimized_away(b(_,_,I)) :- nonmember(do_not_optimize_away,I).
2984
2985 %replace_in_rhs(ID,E,RHS,CleanNewRHS) :- replace_id_by_expr(RHS,ID,E,NewRHS), clean_up(NewRHS,[],CleanNewRHS).
2986
2987 can_be_replaced(RHS,_,Ids) :- get_texpr_id(RHS,RHSID),!,
2988 \+ (member(TID2,Ids), get_texpr_id(TID2,RHSID)). % ID occurs in Ids, replacing it in Expr will move the scope
2989 % example: Z Test (\LET x==1 @ (\LET x==x+1; y==x @ 7*x+y)) = 15
2990 can_be_replaced(_RHS,UsedIds,Ids) :- % TO DO: compute used ids
2991 %find_identifier_uses(RHS,[],UsedIds),
2992 get_texpr_ids(Ids,AtomicIds), sort(AtomicIds,SortedIds),
2993 \+ ord_intersect(UsedIds,SortedIds).
2994
2995 % simplify LET Ids BE Ids=Exprs IN Expr END by moving one identifier Id inside
2996 simplify_let(Ids,Exprs,Expr,NIds,NExprs,CleanNewExpr) :-
2997 ? nth0(N,Ids,TId,NIds),
2998 get_texpr_id(TId,Id),
2999 can_be_optimized_away(TId),
3000 nth0(N,Exprs,LetExpr,NExprs),
3001 % we are attempting to optimize Id = LetExpr away
3002 find_identifier_uses_if_necessary(LetExpr,[],LetExprIds),
3003 \+ ord_member(Id,LetExprIds), %\+ occurs_in_expr(Id,LetExpr), % illegal let, e.g., i = i+1;
3004 can_be_replaced(LetExpr,LetExprIds,Ids), % moving LetExpr will not produce scoping issues
3005 maplist(not_occurs_in_expr(Id),NExprs), % The ID is not used for defining other RHS in the same let
3006 simplify_let_aux(TId,Id,LetExpr,LetExprIds,Expr,CleanNewExpr).
3007
3008 simplify_let_aux(_TId,Id,LetExpr,_,Expr,CleanNewExpr) :-
3009 % remove let identifiers whose definitions are very simple, i.e. identifiers
3010 is_simple_expression(LetExpr),
3011 % TId = LetExpr
3012 % TO DO: do not do this to outer variables which the user cares about !!
3013 !,
3014 %maplist(replace_in_rhs(Id,LetExpr),NExprs,NewExprs),
3015 replace_id_by_expr(Expr,Id,LetExpr,NExpr),
3016 NonGroundExceptions = do_not_ground_types, % see test 2493; TODO: should we pass NonGroundExceptions as parameter?
3017 clean_up(NExpr,NonGroundExceptions,CleanNewExpr). % clean up adjust eg used_ids info; necessary for test 568 in prob_safe_mode
3018 simplify_let_aux(TId,Id,LetExpr,LetExprIds,Expr,CleanNewExpr) :-
3019 % push the let expression down the AST. E.g. "LET a=E IN (4*(a+z*a) + z)" would
3020 % be transformed to "4*(LET a=E IN (a+z*a)) + z"
3021 % If the final form is like "LET a=E IN a" it will be simplified to E.
3022 \+ do_not_to_move_let_inside(Expr),
3023 !,
3024 ( identifier_sub_ast(Expr,Id,SubPosition) ->
3025 prune_sub_ast_pos_list(SubPosition,Id,LetExpr,LetExprIds,Expr,SafeSubPosition),
3026 SafeSubPosition = [_|_], % The LET can actually be moved down
3027 % SubPosition points to the highest point in the AST coveringa all occurences
3028 exchange_ast_position(SafeSubPosition,Expr,OldInner,NewInner,NExpr),
3029 get_texpr_type(OldInner,Type),
3030 ( get_texpr_id(OldInner,Id) -> % There is only one reference to Id,
3031 debug_format(19,'Simplified LET for ~w away, single usage~n',[Id]),
3032 NewInner = LetExpr % replace it with the expression
3033 % We need to check that we are not simply just exchanging lets with each other
3034 ; cycle_detection(Id,SafeSubPosition,Expr) -> debug_println(9,cycle(Id)),fail
3035 ; Type=pred -> % print(created_let_predicate(N,Id,SafeSubPosition,NIds)),nl,
3036 extract_important_info_from_subexpressions(LetExpr,OldInner,NewLetInfo), % maybe no longer necessary because of cleanups call below
3037 create_texpr(let_predicate([TId],[LetExpr],OldInner),pred,NewLetInfo,NewInner)
3038 ; % print(create_let_expression(TId)),nl,
3039 extract_important_info_from_subexpressions(LetExpr,OldInner,NewLetInfo), % maybe no longer necessary because of cleanups call below
3040 create_texpr(let_expression([TId],[LetExpr],OldInner),Type,NewLetInfo,NewInner)),
3041 NonGroundExceptions = do_not_ground_types, % see test 2493
3042 clean_up(NExpr,NonGroundExceptions,CleanNewExpr) % maybe we only need WD post rules ?
3043 ; always_well_defined_or_wd_improvements_allowed(LetExpr) ->
3044 % Id does not occur in the expression -> just remove the LET
3045 add_debug_message(b_ast_cleanup,'Identifier of LET not used: ',Id,Expr),
3046 CleanNewExpr = Expr
3047 ; gen_unused_let_id_msg(TId,Id,LetExpr),fail
3048 ),!.
3049 simplify_let_aux(TId,Id,LetExpr,_LetExprIds,Expr,CleanNewExpr) :- % do_not_to_move_let_inside has succeeded
3050 not_occurs_in_expr(Id,Expr),
3051 (always_well_defined_or_wd_improvements_allowed(LetExpr)
3052 -> add_debug_message(b_ast_cleanup,'Identifier of LET not used: ',Id,Expr)
3053 ; gen_unused_let_id_msg(TId,Id,LetExpr), fail
3054 ),
3055 CleanNewExpr=Expr.
3056
3057 gen_unused_let_id_msg(TID,Id,LetExpr) :- get_texpr_info(TID,Infos), member(generated_exists_parameter,Infos),!,
3058 add_debug_message(b_ast_cleanup,'Cannot remove unused identifier in (auto-generated) LET due to WD condition: ',Id,LetExpr).
3059 gen_unused_let_id_msg(_TID,Id,LetExpr) :-
3060 add_message(b_ast_cleanup,'Cannot remove unused identifier in LET due to WD condition: ',Id,LetExpr).
3061
3062
3063 % this has to be checked not just at the top-level but along the SubPosition path
3064 do_not_to_move_let_inside(b(E,_T,Infos)) :-
3065 (do_not_to_move_let_inside_aux(E) -> true
3066 ; E=exists(_,_), (get_preference(lift_existential_quantifiers,true) ; member(allow_to_lift_exists,Infos))
3067 ).
3068 %add_message(simplify_let,'Not moving LET inside: ',b(E,_T,Infos),Infos).
3069 % exists can be lifted which can also lead to duplication allow_to_lift_exists
3070 % for those quantifiers we may duplicate the computation of a let by moving it inside:
3071 do_not_to_move_let_inside_aux(forall(_,_,_)). % otherwise we may compute the let multiple times
3072 do_not_to_move_let_inside_aux(comprehension_set(_,_)). % ditto
3073 do_not_to_move_let_inside_aux(general_product(_,_,_)). % ditto (PI)
3074 do_not_to_move_let_inside_aux(general_sum(_,_,_)). % ditto (SIGMA)
3075 do_not_to_move_let_inside_aux(lambda(_,_,_)). % ditto
3076 do_not_to_move_let_inside_aux(quantified_intersection(_,_,_)). % ditto
3077 do_not_to_move_let_inside_aux(quantified_union(_,_,_)). % ditto
3078 %do_not_to_move_let_inside_aux(exists(_,_)). % can lead to duplication upon lifting or semi-lifting, e.g., if two exists are nested?
3079 % TODO: investigate if we should disable moving LET into exists in general
3080 do_not_to_move_let_inside_aux(convert_bool(_)). % can lead to duplication if reification fails
3081
3082
3083 not_occurs_in_expr(Id,Expr) :- \+ occurs_in_expr(Id,Expr).
3084
3085 cycle_detection(Id,SubPosition,Expr) :-
3086 ? (get_constructor(SubPosition,Expr,CC),
3087 \+ let_constructor(CC) -> fail % at least one other constructor found
3088 ; debug_println(9,cycle_let_detection(Id))
3089 ).
3090 % we have a let constructor which can be modified by simplify_let:
3091 let_constructor(let_expression).
3092 let_constructor(let_predicate).
3093 let_constructor(let_substitution).
3094
3095 % traverse a SubPositions List from identifier_sub_ast and check for potential duplication
3096 % by quantifiers, if found: prune list at that point
3097 prune_sub_ast_pos_list([],_,_,_,_,[]).
3098 prune_sub_ast_pos_list([Pos|T],Id,LetExpr,LetExprIds,OldTExpr,Res) :-
3099 remove_bt(OldTExpr,OldExpr,NewExpr,_NewTExpr),
3100 (do_not_to_move_let_inside(OldTExpr)
3101 -> Res=[],
3102 add_debug_message(simplify_let,'Not moving LET inside quantifier/bool: ',Id,OldTExpr)
3103 ; syntaxtransformation(OldExpr,Subs,TNames,_NSubs,NewExpr),
3104 get_texpr_ids(TNames,Names),
3105 sort(Names,SNames),
3106 (ord_intersect(LetExprIds,SNames)
3107 -> Res=[], % we would introduce variable capture problems with the LetExpr if we move it inside
3108 %TODO: we could rename local quantifier names to resolve clash
3109 add_debug_message(simplify_let,'Not moving LET inside quantifier due to name clash: ',Id,OldTExpr)
3110 ; Res = [Pos|TR],
3111 nth0(Pos,Subs, OldSelected,_Rest),
3112 prune_sub_ast_pos_list(T,Id,LetExpr,LetExprIds,OldSelected,TR),
3113 (TR=[], avoid_top_level_let_within(OldExpr)
3114 -> Res=[] % we would create a let at the top-level of this
3115 ; Res = [Pos|TR]
3116 )
3117 )
3118 ).
3119
3120 avoid_top_level_let_within(card(_)). % reification of card does not work yet with top-level let_predicate, for test 1562
3121 % Note: maybe it is best to avoid pushing into card completely, as even at the next
3122 % level the let could disturb the reification small set detection?
3123
3124 % get a SubPosition path (as produced by identifier_sub_ast) and
3125 % generate upon backtracking all constructors that are used along the Path
3126 get_constructor([Pos|T],OldTExpr,ConstructorForPos) :-
3127 remove_bt(OldTExpr,OldExpr,NewExpr,_NewTExpr),
3128 (functor(OldExpr,ConstructorForPos,_)
3129 ; syntaxtransformation(OldExpr,Subs,_Names,_NSubs,NewExpr),
3130 nth0(Pos,Subs, OldSelected,_Rest),
3131 get_constructor(T,OldSelected,ConstructorForPos)
3132 ).
3133
3134 % simple expressions that can be inlined without problem
3135 is_simple_expression(TExpr) :-
3136 get_texpr_expr(TExpr,Expr),
3137 is_simple_expression2(Expr),!.
3138 is_simple_expression(TExpr) :- is_just_type(TExpr).
3139
3140 is_simple_expression2(identifier(_)).
3141 is_simple_expression2(integer(_)).
3142 is_simple_expression2(integer_set(_)). % NATURAL or NATURAL1
3143 is_simple_expression2(real(_)).
3144 is_simple_expression2(string(_)).
3145 is_simple_expression2(boolean_true).
3146 is_simple_expression2(boolean_false).
3147 is_simple_expression2(empty_set).
3148 is_simple_expression2(empty_sequence).
3149 is_simple_expression2(couple(A,B)) :- is_simple_expression(A), is_simple_expression(B).
3150 is_simple_expression2(value(_)). % is already computed; no problem in duplicating
3151 %is_simple_expression2(interval(Low,Up)) :- is_simple_expression(Low), is_simple_expression(Up).
3152 % TODO: simple records?
3153 % simple set_extension or sequence_extensions have already been pre-computed to value(avl_set(.))
3154
3155
3156 % a variation of is_simple_expression, allowing some simple constructs
3157 is_simple_expression_lvl(TExpr,Lvl) :-
3158 get_texpr_expr(TExpr,Expr),
3159 is_simple_expression2_lvl(Expr,Lvl),!.
3160 is_simple_expression2_lvl(Cons,Lvl) :- simple_binary_constructor(Cons,A,B), !,
3161 Lvl>0, L1 is Lvl-1,
3162 is_simple_expression_lvl(A,L1),is_simple_expression_lvl(B,L1).
3163 is_simple_expression2_lvl(Cons,Lvl) :- simple_unary_constructor(Cons,A),!,
3164 Lvl>0, L1 is Lvl-1,
3165 is_simple_expression_lvl(A,L1).
3166 is_simple_expression2_lvl(E,_) :- is_simple_expression2(E).
3167
3168 simple_binary_constructor(function(A,B),A,B).
3169 simple_binary_constructor(couple(A,B),A,B).
3170 % TODO: simple records?
3171 simple_unary_constructor(reverse(A),A).
3172 simple_unary_constructor(set_extension([A]),A).
3173 simple_unary_constructor(sequence_extension([A]),A).
3174
3175 % check for duplication of complex LHS expressions by replacing ID with LHS NrOccurences of times for Rule
3176 is_replace_id_by_expr_ok(_LHS,_ID,NrOccurences,_Rule) :- NrOccurences < 2.
3177 is_replace_id_by_expr_ok(_LHS,_ID,_,_Rule) :-
3178 get_preference(normalize_ast,true),!. % is necessary at least for remove_member_comprehension, remove_not_member_comprehension
3179 is_replace_id_by_expr_ok(_LHS,_ID,_,_Rule) :-
3180 get_preference(use_common_subexpression_elimination,true),!.
3181 is_replace_id_by_expr_ok(LHS,_ID,NrOccurences,_Rule) :-
3182 (NrOccurences<3 -> Lvl=2 ; NrOccurences<6 -> Lvl=1 ; Lvl=0), % what heuristic should we use here; test 1750 seems to indicate that Lvl=4 would still be beneficial for NrOccurences=2 and lambda_guard1 rule
3183 is_simple_expression_lvl(LHS,Lvl).
3184 is_replace_id_by_expr_ok(LHS,ID,Count,Rule) :-
3185 debug_mode(on),
3186 format('replace ~w (~w times) not ok for ~w using: ',[ID,Count,Rule]), translate:print_bexpr(LHS),nl,fail.
3187
3188
3189 % detect either Event-B identity or id over full type
3190 is_event_b_identity(b(X,_,_)) :- is_event_b_identity_aux(X).
3191 is_event_b_identity_aux(event_b_identity).
3192 is_event_b_identity_aux(identity(T)) :- is_just_type(T).
3193
3194 :- use_module(library(avl),[avl_member/2]).
3195 % check if we have a set extension and return list of terms
3196 is_set_extension(b(S,T,I),L) :- is_set_extension_aux(S,T,I,L).
3197 is_set_extension_aux(set_extension(L),_,_,L).
3198 % TO DO: detect sequence_extension
3199 is_set_extension_aux(value(avl_set(A)),SetType,I,L) :- % computed by eval_set_extension
3200 is_set_type(SetType,Type),
3201 findall(b(value(M),Type,I),avl_member(M,A),L).
3202
3203 is_sequence_extension(b(S,T,I),L) :- is_sequence_extension_aux(S,T,I,L).
3204 is_sequence_extension_aux(sequence_extension(L),_,_,L).
3205 % TO DO: detect value/set_extensions
3206
3207 recursion_detection_enabled(A,B,I) :-
3208 ? (recursion_detection_enabled_aux(A,B,I) -> true).
3209 recursion_detection_enabled_aux(A,_B,I) :-
3210 animation_mode(b), % in B mode,
3211 memberchk(section(properties),I), % the rule should be only applied to properties
3212 % and where A is an abstract constant.
3213 get_texpr_info(A,AInfo),memberchk(loc(_,_,abstract_constants),AInfo).
3214 recursion_detection_enabled_aux(A,_B,_I) :-
3215 animation_minor_mode(eventb), % in Event-B,
3216 %TODO: Limit application to axioms
3217 get_texpr_info(A,AInfo), % A must be a constant
3218 memberchk(loc(_,constants),AInfo).
3219 recursion_detection_enabled_aux(_A,_B,_I) :-
3220 animation_minor_mode(z). % use always in Z
3221 recursion_detection_enabled_aux(_A,_B,Infos) :- % check for @desc recursive_let pragma
3222 member(description(D),Infos), rec_let_pragma(D).
3223
3224 rec_let_pragma(recursive_let).
3225 rec_let_pragma(letrec). % more compact exists in other languages
3226 rec_let_pragma(reclet). % Z syntax
3227
3228 % peel of relational compositions and replace with function application
3229 % used to translate (F;G)(X) --> G(F(X)) or (F;G;H)(X) --> H(G(F(X))) ...
3230 peel_rel_composition(b(composition(F,G),_,_),TypeGArg,Info,Arg,GFres,L1) :-
3231 get_texpr_type(G,SType),
3232 bsyntaxtree:is_set_type(SType,couple(TypeFArg,_)),
3233 !,
3234 %Note: rule is multi: we will detect compositions inside G at next iteration
3235 peel_rel_composition(F,TypeFArg,Info,Arg,FRes,Level), L1 is Level+1,
3236 safe_create_function_call(G,FRes,TypeGArg,Info,GFres).
3237 peel_rel_composition(Fun,TypeFArg,Info,Arg,FArg,0) :-
3238 safe_create_function_call(Fun,Arg,TypeFArg,Info,FArg). % TODO: apply function_call_opt
3239
3240 % create a function call and detect certain optimisations like lambda inlining
3241 safe_create_function_call(Fun,Arg,TypeFArg,Info,Res) :-
3242 safe_create_texpr(function(Fun,Arg),TypeFArg,Info,FArg),
3243 (cleanup_pre_function(Fun,Arg,TypeFArg,Info,CleanupRes,Info2,_)
3244 -> Res = b(CleanupRes,TypeFArg,Info2)
3245 ; Res=FArg
3246 ).
3247
3248
3249 construct_union_from_list([X],_,_,Res) :- !, Res=X.
3250 construct_union_from_list([X,Y|T],Type,Info,Res) :-
3251 construct_union_from_list([Y|T],Type,Info,RHS),
3252 Res = b(union(X,RHS),Type,Info).
3253 construct_inter_from_list([X],_,_,Res) :- !, Res=X.
3254 construct_inter_from_list([X,Y|T],Type,Info,Res) :-
3255 construct_inter_from_list([Y|T],Type,Info,RHS),
3256 Res = b(intersection(X,RHS),Type,Info).
3257
3258 % LEQ_SYM_BREAK / LEQ_SYM does not support all types yet:
3259 sym_break_supported_type(Var) :- var(Var),!,fail.
3260 sym_break_supported_type(integer).
3261 sym_break_supported_type(boolean).
3262 sym_break_supported_type(real).
3263 sym_break_supported_type(string).
3264 sym_break_supported_type(global(_)).
3265 sym_break_supported_type(couple(A,B)) :- sym_break_supported_type(A), sym_break_supported_type(B).
3266 sym_break_supported_type(record(F)) :- maplist(sym_break_supported_field,F).
3267 sym_break_supported_field(field(_,T)) :- sym_break_supported_type(T).
3268 % example with pairs: !(x,y).(x:s2 & y:s2 & x/=y => prj1(INTEGER,INTEGER)(x)/=prj1(INTEGER,INTEGER)(y)) (with let s2 = {x,y|x:1..10000 & y:{x+1}}); here we get a slow-down; maybe we should check if RHS complicated enough
3269 % here it is beneficial: !(x,y).(x:dom(s)&y:dom(s)&x/=y => s(x)+s(y)>0) with let s={x,y,v|x:1..10&y:1..50&v=x+y}; runtime goes down from 9.7 to 6.1 seconds
3270
3271 ?construct_sym_break(integer,TID1,TID2,Pred,Res) :- member_in_conjunction(Neq,Pred), is_id_inequality(Neq,TID1,TID2),
3272 !, get_texpr_info(TID1,Info1),
3273 Res = b(less(TID1,TID2),pred,Info1). % we don't need the external function; we can used <
3274 construct_sym_break(integer,TID1,TID2,_Pred,Res) :- !, get_texpr_info(TID1,Info1),
3275 Res = b(less_equal(TID1,TID2),pred,Info1). % we don't need the external function; we can used <= ; we could check whether < or <= already in Pred ? occurs in test 1360: #(vv,ww).(vv < ww & ww < vv)
3276 construct_sym_break(_,TID1,TID2,_,Res) :- get_texpr_info(TID1,Info1),
3277 Res = b(external_pred_call('LEQ_SYM',[TID1,TID2]),pred,Info1).
3278
3279 % construct {Expr1,Expr2}
3280 construct_set_extension(Expr1,Expr2,Res) :- same_texpr(Expr1,Expr2),!,
3281 get_texpr_type(Expr1,Type),
3282 extract_info(Expr1,Infos), % will also copy used_ids; ok as this does not change by adding set_extension
3283 Res = b(set_extension([Expr1]),set(Type),Infos).
3284 construct_set_extension(Expr1,Expr2,Res) :-
3285 get_texpr_type(Expr1,Type),
3286 extract_info(Expr1,Expr2,Infos),
3287 (Expr1 @=< Expr2 -> Lst=[Expr1,Expr2] ; Lst=[Expr2,Expr1]), % solves issue with ParserTests; {FALSE,TRUE}
3288 Res = b(set_extension(Lst),set(Type),Infos).
3289
3290 l_construct_set_extension([E1],[E2],Res) :- !, construct_set_extension(E1,E2,Res).
3291 l_construct_set_extension(L1,L2,Res) :- append(L1,L2,L12),
3292 L12=[Expr1|T],!,
3293 get_texpr_type(Expr1,Type),
3294 last(T,Expr2),
3295 extract_info(Expr1,Expr2,Infos), % TODO: improve info extraction; sort L12 values?
3296 Res = b(set_extension(L12),set(Type),Infos).
3297 l_construct_set_extension(A,B,Res) :-
3298 add_internal_error('Requires at least two els:',l_construct_set_extension(A,B,Res)),fail.
3299
3300 is_id_inequality(b(not_equal(A,B),pred,_),X,Y) :-
3301 (same_texpr(A,X) -> same_texpr(B,Y) ; same_texpr(A,Y), same_texpr(B,X)).
3302
3303
3304 %known_set(b(integer_set(_),set(integer),_)). % used for forall splitting; TODO: use is_integer_set ??
3305 %known_set(b(interval(_,_),set(integer),_)).
3306
3307 % detect if we have an if-then-else function (which is then applied to a dummy argument)
3308 is_if_then_else(b(comprehension_set([TDummyID1,ID2],CONJ),_Type,_),_,_DUMMYARG,IFPRED,THEN,ELSE) :-
3309 % we ignore the _DUMMYARG as here we do not check the value of DUMMYARG in the body
3310 % TO DO: also allow removal of equalities as in TLA case below
3311 % DETECT {Dummy,Res| Test => Res=THEN & not(Test) => Res=ELSE}
3312 get_texpr_id(ID2,LambdaID), get_texpr_id(TDummyID1,DummyID),
3313 is_ifte_case_conjunct(CONJ,IFPRED,EQ1,EQ2),
3314 is_equality_conj(EQ1,II1,THEN), get_texpr_id(II1,LambdaID),
3315 is_equality_conj(EQ2,II2,ELSE), get_texpr_id(II2,LambdaID),
3316 \+ occurs_in_expr(DummyID,IFPRED),\+ occurs_in_expr(LambdaID,IFPRED),
3317 \+ occurs_in_expr(DummyID,THEN), \+ occurs_in_expr(LambdaID,THEN),
3318 \+ occurs_in_expr(DummyID,ELSE), \+ occurs_in_expr(LambdaID,ELSE).
3319
3320 % Recognize B2TLA encodings as well: %((x).(x=0 & PRED|C1)\/%(x).(x=0 & not(PRED)|C2)) (0)
3321 %is_if_then_else(IF,_,_,_,_) :- nl,print(IF),nl,nl,fail.
3322 is_if_then_else(b(union(COMP1,COMP2),_Type,_),_,DUMMYARG,IFPRED,THEN,ELSE) :-
3323 if_then_else_lambda(COMP1,DUMMYARG,IFPRED,THEN),
3324 if_then_else_lambda(COMP2,DUMMYARG,NOT_IFPRED,ELSE),
3325 is_negation_of(IFPRED,NOT_IFPRED).
3326
3327 is_if_then_else(b(set_extension([CASE1,CASE2]),_,Info),POST,Arg,IFPRED,THEN,ELSE) :-
3328 (POST=post -> true ; data_validation_mode), % in cleanup_pre the wd info is not yet computed
3329 Arg = b(convert_bool(IFPRED),boolean,_),
3330 % {TRUE|->v1,FALSE|->v2}(bool(TEST)) --> IF TEST THEN v1 ELSE v2 END
3331 % example: {TRUE|->1,FALSE|->2}(bool(2>3)) --> IF 2>3 THEN 1 ELSE 2 END
3332 CASE1 = b(couple(b(B1,boolean,_),Val1),_,_),
3333 CASE2 = b(couple(b(B2,boolean,_),Val2),_,_),
3334 ( B1=boolean_true,B2=boolean_false -> THEN=Val1,ELSE=Val2
3335 ; B2=boolean_true,B1=boolean_false -> THEN=Val2,ELSE=Val1),
3336 (always_well_defined_or_disprover_mode(THEN),
3337 always_well_defined_or_disprover_mode(ELSE) -> true
3338 %,add_message(function_if_then_else,'Detected {TRUE|->v1,FALSE|->v2}(bool(TEST)) construct without WD condition, rewriting it to IF TEST THEN v1 ELSE v2 END','',Info)
3339 % otherwise: the transformation to IF-THEN-ELSE may remove WD problem
3340 ; silent_mode(on) -> true
3341 ; %data_validation_mode,
3342 add_message(function_if_then_else,'Detected {TRUE|->v1,FALSE|->v2}(bool(TEST)) construct with WD condition, you should probably rewrite it to IF TEST THEN v1 ELSE v2 END','',Info),
3343 true % fail
3344 ).
3345
3346 if_then_else_lambda(b(comprehension_set([TDummyID,TLAMBDAID],CONJ),_,_),DUMMYARG,IFPRED,RESULT) :-
3347 conjunction_to_nontyping_list(CONJ,CL),
3348 get_texpr_id(TDummyID,DummyID),
3349 get_texpr_id(TLAMBDAID,LambdaID),
3350 (remove_equality(DummyID,DUMMYVAL,CL,ConjList) % look if there is an equality for the DummyID
3351 -> same_texpr(DUMMYVAL,DUMMYARG), % TO DO: check that we apply the function with this value
3352 \+ occurs_in_expr(LambdaID,DUMMYVAL), % ensure this is really the same value
3353 \+ occurs_in_expr(DummyID,DUMMYVAL)
3354 ; ConjList=CL),
3355 remove_equality(LambdaID,RESULT,ConjList,RestList),
3356 \+ occurs_in_expr(LambdaID,RESULT),
3357 \+ occurs_in_expr(DummyID,RESULT), % otherwise this is not really a dummy identifier
3358 conjunct_predicates_with_pos_info(RestList,IFPRED),
3359 \+ occurs_in_expr(DummyID,IFPRED).
3360
3361 % DETECT (IFPRED => EQ1) & (not(IFPRED) => EQ2)
3362 is_ifte_case_conjunct(CONJ,IFPRED,EQ1,EQ2) :-
3363 is_a_conjunct(CONJ,IMP1,IMP2),
3364 is_an_implication_conj(IMP1,IFPRED,EQ1),
3365 is_an_implication_conj(IMP2,NOT_IFPRED,EQ2),
3366 is_negation_of(IFPRED,NOT_IFPRED).
3367 % TO DO: also deal with lazy_lets wrapped around
3368 %is_ifte_case_conjunct(lazy_let_pred(ID,IFPRED,CONJ),IFPRED,EQ1,EQ2) :-
3369 % is_a_conjunct(CONJ,IMP1,IMP2),
3370 % is_an_implication(IMP1,IFPRED,EQ1),
3371 % is_an_implication(IMP2,NOT_IFPRED,EQ2),
3372 % is_negation_of(IFPRED,NOT_IFPRED).
3373
3374 % detect explicit if-then-else for cleanup_post:
3375 explicit_if_then_else(if_then_else(IF,THEN,ELSE),IF,THEN,ELSE).
3376 explicit_if_then_else(if([b(if_elsif(IF,THEN),subst,_)|TAIL]),IF,THEN,ELSE) :-
3377 (TAIL = [b(if_elsif(b(truth,pred,_),EE),subst,_)|_] -> ELSE = EE
3378 ; TAIL = [] -> ELSE = b(skip,subst,[generated])
3379 ).
3380
3381 % just like is_an_implication but allow typing conjuncts (not yet removed in pre-phase)
3382 is_an_implication_conj(Pred,LHS,RHS) :- is_an_implication(Pred,LHS,RHS),!.
3383 is_an_implication_conj(b(conjunct(A,B),pred,_),LHS,RHS) :-
3384 (is_typing_predicate(A) -> is_an_implication_conj(B,LHS,RHS)
3385 ; is_typing_predicate(B) -> is_an_implication_conj(A,LHS,RHS)).
3386
3387 % just like is_equality but allow typing conjuncts (not yet removed in pre-phase)
3388 is_equality_conj(EQ,LHS,RHS) :- is_equality(EQ,LHS,RHS).
3389 is_equality_conj(b(conjunct(A,B),pred,_),LHS,RHS) :-
3390 (is_typing_predicate(A) -> is_equality_conj(B,LHS,RHS)
3391 ; is_typing_predicate(B) -> is_equality_conj(A,LHS,RHS)).
3392
3393 % remove a dummy equality from list
3394 remove_equality(ID,RHS,ConjList,Rest) :-
3395 DummyEQ = b(equal(DID,RHS),pred,_), % TO DO: also accept simple typing memberships ?
3396 get_texpr_id(DID,ID),
3397 select(DummyEQ,ConjList,Rest). %, print(eq(DID,RHS)),nl.
3398
3399 is_typing_conjunct(b(member(_,B),_,_)) :- is_just_type(B).
3400 is_typing_predicate(Typing) :- conjunction_to_list(Typing,LT), maplist(is_typing_conjunct,LT).
3401 conjunction_to_nontyping_list(Pred,List) :- conjunction_to_list(Pred,TList), exclude(is_typing_conjunct,TList,List).
3402
3403 % used e.g. for translating : ran({x1,...xn|P}) --> {xn| #(x1,...).(P)}
3404 % we annotate this exists as allow_to_lift; as the origin is a set comprehension which originally had all variables at the top-level (including the existentially quantified ones)
3405 create_outer_exists_for_dom_range(Ids,CompPred,NewCompPred) :-
3406 create_outer_exists_for_dom_range2(Ids,CompPred,NewCompPred1),
3407 compute_used_ids_info_if_necessary(NewCompPred1,NewCompPred).
3408 create_outer_exists_for_dom_range2(Ids,b(exists(InnerIds,P),pred,Infos),New) :-
3409 member(allow_to_lift_exists,Infos),
3410 append(Ids,InnerIds,NewIds),!, % simply add Ids to existing existential quantifier
3411 New = b(exists(NewIds,P),pred,NewInfos),
3412 remove_from_used_ids(Infos,Ids,NewInfos).
3413 create_outer_exists_for_dom_range2(Ids,P,New) :-
3414 % we could also use construct_optimized_exists/3 it does a full partitioning of P; see also components_partition_exists rule above
3415 create_exists_opt_liftable(Ids,P,New). % marked as liftable as origin is a set comprehension with all ids
3416 % calls create_exists_opt: detects also simple tautologies like #x.(x=E)
3417 % + predicates that do not use one of the quantified identifiers are moved outside
3418 %N=b(P2,T2,[allow_to_lift_exists|I2]). %, check_ast(N).
3419
3420 % remove newly quantified Typed IDs from used_ids info; if the info exists
3421 remove_from_used_ids(OldInfo,NewQuantifiedTIds,NewInfo) :-
3422 select(used_ids(OldUsed),OldInfo,I1),!,
3423 get_texpr_ids(NewQuantifiedTIds,NewQ),
3424 NewInfo = [used_ids(NewUsed)|I1],
3425 ord_subtract(OldUsed,NewQ,NewUsed).
3426 remove_from_used_ids(I,_,I).
3427
3428 % if _lambda_result_ occurs in list; rename it so that we do not get issues with enumeration
3429 rename_lambda_result_id(Ids,CompPred,NewIds,NewCompPred) :-
3430 ? select(ID,Ids,Rest),
3431 get_texpr_id(ID,'_lambda_result_'),
3432 !,
3433 get_unique_id_inside('__RANGE_LAMBDA__',CompPred,FRESHID), % if we don't rename then _lambda_result_ will not be enumerated ! TO DO: also check different from Ids if we want to remove __ prefix
3434 % TO DO: remove lambda_result(Info)
3435 ? rename_bt(CompPred,[rename('_lambda_result_',FRESHID)],NewCompPred),
3436 get_texpr_type(ID,IDType), get_texpr_info(ID,IDInfo),
3437 NewIds = [b(identifier(FRESHID),IDType,IDInfo)|Rest].
3438 rename_lambda_result_id(Ids,CompPred,Ids,CompPred).
3439
3440 contains_predicate(convert_bool(Pred),boolean,Pred,
3441 convert_bool(NewP),NewP).
3442 contains_predicate(comprehension_set(CompIds,Pred),_,Pred,
3443 comprehension_set(CompIds,NewP),NewP).
3444 contains_predicate(general_sum(Ids,Pred,Expression),integer,Pred,
3445 general_sum(Ids,NewP,Expression),NewP).
3446 contains_predicate(general_product(Ids,Pred,Expression),integer,Pred,
3447 general_product(Ids,NewP,Expression),NewP).
3448 contains_predicate(if_then_else(Pred,Then,Else),_,Pred,
3449 if_then_else(NewP,Then,Else),NewP).
3450 contains_predicate(assertion_expression(Pred,ErrMsg,Expr),_,Pred,
3451 assertion_expression(NewP,ErrMsg,Expr),NewP).
3452 contains_predicate(precondition(Pred,Body),subst,Pred,
3453 precondition(NewP,Body),NewP).
3454 contains_predicate(assertion(Pred,Body),subst,Pred,
3455 assertion(NewP,Body),NewP).
3456 contains_predicate(witness_then(Pred,Body),subst,Pred,
3457 witness_then(NewP,Body),NewP).
3458 contains_predicate(becomes_such(Vars,Pred),subst,Pred,
3459 becomes_such(Vars,NewP),NewP).
3460 contains_predicate(any(Parameters,Pred,Body),subst,Pred,
3461 any(Parameters,NewP,Body),NewP).
3462 contains_predicate(lazy_let_expr(ID,SharedExpr,MainExpr),pred, SharedExpr,
3463 lazy_let_expr(ID,NewSharedExpr,MainExpr), NewSharedExpr) :-
3464 get_texpr_type(SharedExpr,pred).
3465 contains_predicate(lazy_let_subst(ID,SharedExpr,MainExpr),pred, SharedExpr,
3466 lazy_let_subst(ID,NewSharedExpr,MainExpr), NewSharedExpr) :-
3467 get_texpr_type(SharedExpr,pred).
3468 contains_predicate(lazy_let_pred(ID,SharedExpr,MainExpr),pred, MainExpr,
3469 lazy_let_pred(ID,SharedExpr,NewMainExpr), NewMainExpr) :-
3470 \+ get_texpr_type(SharedExpr,pred).
3471 % while(COND,STMT,INV,VARIANT), select, if --> can have multiple predicates !!
3472 contains_predicates(while(Cond, Stmt,Invariant,Variant),subst, [Cond,Invariant],
3473 while(NewCond,Stmt,NewInv, Variant), [NewCond,NewInv]).
3474 contains_predicates(if(Whens),subst,Preds,
3475 if(NewWhens),NewPreds) :-
3476 get_predicates_from_list_of_cases(Whens,Preds,NewWhens,NewPreds).
3477 contains_predicates(select(Whens),subst,Preds,
3478 select(NewWhens),NewPreds) :-
3479 get_predicates_from_list_of_cases(Whens,Preds,NewWhens,NewPreds).
3480 contains_predicates(select(Whens,Else),subst,Preds,
3481 select(NewWhens,Else),NewPreds) :-
3482 get_predicates_from_list_of_cases(Whens,Preds,NewWhens,NewPreds).
3483 contains_predicates(lazy_let_pred(ID,SharedExpr,MainExpr),pred, [SharedExpr,MainExpr],
3484 lazy_let_pred(ID,NSharedExpr,NMainExpr), [NSharedExpr,NMainExpr]) :-
3485 get_texpr_type(SharedExpr,pred).
3486
3487 get_predicates_from_list_of_cases([],[],[],[]).
3488 get_predicates_from_list_of_cases([H|T],Preds,[NewH|NewT],NewPreds) :-
3489 (get_single_predicate(H,Pred,NH,NewPred)
3490 -> NewH=NH, Preds=[Pred|TP], NewPreds = [NewPred|NTP]
3491 ; NewH=H, Preds=TP, NewPreds = NTP
3492 ),
3493 get_predicates_from_list_of_cases(T,TP,NewT,NTP).
3494
3495 get_single_predicate(b(E,T,I),Preds,b(NewE,T,I),NewPreds) :-
3496 get_single_predicate_aux(E,Preds,NewE,NewPreds).
3497 get_single_predicate_aux(select_when(Pred,Body),Pred,select_when(NewPred,Body),NewPred).
3498 get_single_predicate_aux(if_elsif(Pred,Body),Pred,if_elsif(NewPred,Body),NewPred).
3499
3500
3501 % Detect useless statements in sequential compositions:
3502 % useful for LCHIP code, e.g., where dummy code is added for the code generator: i9 : (i9 : BOOL); i9 := TRUE
3503 % in test 1660 we remove an assignment that reads an unitialised variable
3504 filter_useless_subst_in_sequence([],_,R) :- !, R=[].
3505 filter_useless_subst_in_sequence([S1],_,R) :- !, R=[S1].
3506 filter_useless_subst_in_sequence([S1|S2],change,R) :- useless_subst_in_sequence(S1,S2),!,
3507 add_hint_message(filter_useless_subst_in_sequence,'Removing useless substitution in sequence','',S1),
3508 filter_useless_subst_in_sequence(S2,_,R).
3509 filter_useless_subst_in_sequence([S1|S2],Change,[S1|RS]) :- filter_useless_subst_in_sequence(S2,Change,RS).
3510
3511 useless_subst_in_sequence(b(Subst,subst,Info),Sequence2) :- % print(check(Subst,Sequence2)),nl,
3512 useless_code_before_sequence(Subst,Info,Sequence2).
3513
3514 useless_code_before_sequence(skip,_,_) :- !.
3515 useless_code_before_sequence(Subst,_,SubstList) :- is_non_failing_assignment(Subst,TID), def_get_texpr_id(TID,ID),
3516 is_dead(ID,SubstList).
3517
3518 % first naive version to compute if the variable ID is dead when followed by a list of substitutions
3519 is_dead(ID,[b(Subst,subst,Info)|_]) :- % we currently only look at first statement; TO DO: improve
3520 is_dead_aux(Subst,Info,ID).
3521 is_dead_aux(assign_single_id(TID,RHS),_Info,ID) :-
3522 get_texpr_id(TID,ID), % we assign to ID; TO DO: deal with other assignments and assignments to functions f(i) := ...
3523 find_identifier_uses_if_necessary(RHS,[],UsedIds),
3524 \+ ord_member(ID,UsedIds).
3525
3526 % non failing assignment without WD condition, note that we may still try and read identifiers that have not been initialised (having term(undefined) as value), see test 1660
3527 is_non_failing_assignment(becomes_such([TID],Pred),TID) :- is_truth(Pred).
3528 is_non_failing_assignment(becomes_element_of([TID],Set),TID) :- definitely_not_empty_set(Set).
3529 is_non_failing_assignment(assign_single_id(TID,RHS),TID) :- always_well_defined_or_disprover_mode(RHS).
3530 is_non_failing_assignment(assign([LHS],[RHS]),TID) :- always_well_defined_or_disprover_mode(RHS),
3531 get_lhs_assigned_identifier(LHS,TID).
3532
3533
3534 % ---------------------------------------------
3535
3536 :- use_module(extrasrc(b_expression_sharing),[cse_optimize_predicate/2]).
3537 % these are "global" optimizations at the predicate level
3538 % they are only called once a predicate has been completely constructed
3539 predicate_level_optimizations(Pred,NewPred) :-
3540 predicate_level_optimizations(Pred,NewPred,[]).
3541 predicate_level_optimizations(Pred,NewPred,Path) :-
3542 inner_predicate_level_optimizations(Pred,Pred1),
3543 (get_preference(use_common_subexpression_elimination,true),
3544 \+ do_not_optimise_in_context(Path)
3545 -> cse_optimize_predicate(Pred1,NewPred)
3546 ; NewPred=Pred1
3547 ). %,print_opt_debug_info(Pred,NewPred,Path).
3548 /*
3549 print_opt_debug_info(Pred,NewPred,Path) :-
3550 (Pred==NewPred -> true
3551 ; same_texpr(Pred,NewPred) -> true
3552 ; format('Optimized pred ~w: ',[Path]), print_bexpr(NewPred),nl
3553 % , (Path=[] -> trace ; true)
3554 ).
3555 */
3556
3557 do_not_optimise_in_context([path_arg(top_level(invariant),Nr)]) :-
3558 get_preference(use_po,true),
3559 debug_format(19,'% NOT applying CSE to Invariant Nr ~w (PROOF_INFO = TRUE)~n',[Nr]).
3560
3561 :- use_module(partition_detection,[detect_all_partitions_in_predicate/2]).
3562 % this predicate is also called for exists, forall, ...:
3563 inner_predicate_level_optimizations(Pred,NewPred) :-
3564 detect_all_partitions_in_predicate(Pred,NewPred1),
3565 (get_preference(remove_implied_constraints,true)
3566 -> remove_implied_constraints(NewPred1,NewPred)
3567 ; NewPred=NewPred1)
3568 . %,(Pred==NewPred -> true ; print('Optimized pred: '), print_bexpr(NewPred),nl).
3569
3570
3571
3572 % ----------------------------------
3573
3574 remove_implied_constraints(Predicate,NewPredicate) :-
3575 conjunction_to_list(Predicate,PList),
3576 remove_implied_constraints(PList,[],PNew),
3577 conjunct_predicates_with_pos_info(PNew,NewPredicate).
3578
3579 % remove constraints which are redundant for ProB
3580 % example:
3581 % n=1000 & f:1..n --> BOOL & f:1..n +-> BOOL & !x.(x:dom(f) => f(x) = bool(x>50)) & f: 1..n <-> BOOL & dom(f)=1..n
3582 % runtime goes from 500 ms down to 300 ms by remove +->, <-> and dom(f) checks
3583 % but test 1442 has issue: still unclear how useful this static detection is
3584 % it is probably most useful for proving/disproving where we have lots of redundant/derived hypotheses
3585
3586 remove_implied_constraints([],_,[]).
3587 remove_implied_constraints([Constraint|T],SoFar,Result) :-
3588 % print('Checking: '), print_bexpr(Constraint),nl,
3589 possible_implied_constraint(Constraint,C1),
3590 % print('Checking if implied constraint: '), print_bexpr(Constraint),nl,
3591 (member(TC2,T) ; member(TC2,SoFar)),
3592 get_texpr_expr(TC2,C2),
3593 implied_constraint2(C2,C1),
3594 (debug_mode(off) -> true
3595 ; print('Removing implied constraint: '), print_bexpr(Constraint),
3596 print(' <=== '), print_bexpr(TC2),nl),
3597 !,
3598 remove_implied_constraints(T,SoFar,Result).
3599 remove_implied_constraints([H|T],SoFar,[H|RT]) :- remove_implied_constraints(T,[H|SoFar],RT).
3600
3601 possible_implied_constraint(b(E,T,I),E) :-
3602 (possible_implied_constraint2(E,T,I) -> true).
3603 possible_implied_constraint2(member(_,b(FUNCTION,_,_)),_,_) :- functor(FUNCTION,F,2),
3604 (function_implication2(F,_) -> true ; F = relations).
3605 possible_implied_constraint2(equal(b(domain(_),_,_),_),_,_). % TO DO: other way around
3606
3607 % f: A --> B ==> f: A +-> B, f: A<->B, dom(f) = A
3608 % test 1442: issue with surjection
3609 implied_constraint2(member(Fun1,b(FUNCTION1,T,_)), member(Fun2,b(FUNCTION2,T,_))) :-
3610 functor(FUNCTION1,F1,2), arg(1,FUNCTION1,X1), arg(2,FUNCTION1,Y1),
3611 functor(FUNCTION2,F2,2), arg(1,FUNCTION2,X2), arg(2,FUNCTION2,Y2),
3612 function_implication(F1,F2),
3613 same_texpr(Fun1,Fun2),
3614 same_texpr(X1,X2),
3615 same_texpr(Y1,Y2).
3616 % f: A --> B ==> dom(f) = A
3617 implied_constraint2(member(Fun2,b(FUNCTION2,_,_)), equal(b(domain(Fun1),_,_),Domain)) :-
3618 functor(FUNCTION2,F2,2), arg(1,FUNCTION2,Domain2),
3619 total_function(F2),
3620 same_texpr(Fun1,Fun2),
3621 same_texpr(Domain,Domain2).
3622 % TODO: f: A -->> B ==> ran(f) = B ?
3623
3624
3625 total_function(total_bijection).
3626 total_function(total_injection).
3627 total_function(total_surjection).
3628 total_function(total_function).
3629 %total_relation(total_surjection_relation). % not sure if in this case the constraint is maybe not useful after all?
3630 %total_relation(total_relation).
3631
3632 function_implication(F1,F2) :- function_implication2(F1,F2).
3633 function_implication(F1,F2) :- function_implication2(F1,Z), function_implication(Z,F2).
3634
3635 function_implication2(total_bijection,total_injection).
3636 function_implication2(total_bijection,total_surjection).
3637 function_implication2(total_injection,total_function).
3638 function_implication2(total_surjection,total_function).
3639 function_implication2(total_function,partial_function).
3640 function_implication2(partial_function,relations).
3641 function_implication2(partial_injection,partial_function). % >+>
3642 function_implication2(partial_surjection,partial_function).
3643 %function_implication2(partial_bijection,partial_injection).
3644 %function_implication2(partial_bijection,partial_surjection).
3645 function_implication2(total_relation,relations).
3646 function_implication2(surjection_relation,relations).
3647 function_implication2(total_surjection_relation,total_relation).
3648 function_implication2(total_surjection_relation,surjection_relation).
3649
3650 % ------------------
3651
3652
3653 % divide a list of identifiers into domain and range identifiers
3654 get_domain_range_ids([D,R],[D],[R]) :- !.
3655 get_domain_range_ids([D1,D2|T],[D1|DT],R) :- get_domain_range_ids([D2|T],DT,R).
3656
3657
3658 % detect whether there is a pattern of a recursive usage of the identifier: ID(x) or ID[x] or x:ID
3659 find_recursive_usage(TExpr,ID) :-
3660 syntaxtraversion(TExpr,Expr,_,_,Subs,TNames), % print(try_id(ID,Expr,Subs,TNames)),nl,
3661 ( Expr = function(Fun,_), get_texpr_id(Fun,ID) -> true
3662 ; Expr = image(Rel,_), get_texpr_id(Rel,ID) -> true
3663 ; Expr = member(_,Set), get_texpr_id(Set,ID) -> true
3664 ? ; \+ (member(ID1,TNames),get_texpr_id(ID1,ID)), % new local variable with same name
3665 ? member(Sub,Subs), find_recursive_usage(Sub,ID)
3666 ).
3667
3668 :- use_module(bsyntaxtree,[transform_bexpr/3]).
3669 % find comprehension sets and mark them as recursive if they use the recursive ID
3670 mark_recursion(TExpr,RecID,NewTExpr) :-
3671 (transform_bexpr(b_ast_cleanup:mark_comprehension_set(RecID),TExpr,NewTExpr)
3672 -> true
3673 ; add_internal_error('Call failed: ',transform_bexpr(b_ast_cleanup:mark_comprehension_set(RecID),TExpr,NewTExpr)),
3674 NewTExpr=TExpr).
3675
3676 :- public mark_comprehension_set/3.
3677 mark_comprehension_set(RecID,b(lambda(Ids,P,Expr),Type,Info),
3678 b(lambda(Ids,P,Expr),Type,NInfo)) :-
3679 (find_recursive_usage(P,RecID) -> true ; find_recursive_usage(Expr,RecID)),
3680 get_texpr_ids(Ids,AtomicIds),
3681 (silent_mode(on) -> true
3682 ; format('Recursive lambda using ~w detected (name: ~w)~n',[AtomicIds,RecID]),
3683 error_manager:print_message_span(Info),nl
3684 ),
3685 add_texpr_infos(Info,[prob_annotation('SYMBOLIC'),prob_annotation('RECURSIVE')],NInfo).
3686 mark_comprehension_set(RecID,b(comprehension_set(Ids,P),Type,Info),
3687 b(comprehension_set(Ids,P),Type,NInfo)) :-
3688 % DO NOT MARK IT IF IT IS IN RESULT POSITION of recursive function ?
3689 find_recursive_usage(P,RecID),
3690 get_texpr_ids(Ids,AtomicIds),
3691 (silent_mode(on) -> true
3692 ; format('Recursive comprehension set using ~w detected (name: ~w)~n',[AtomicIds,RecID]),
3693 error_manager:print_message_span(Info),nl
3694 ),
3695 NInfo = [prob_annotation('SYMBOLIC'),prob_annotation('RECURSIVE')|Info]. % TO DO: only add if not already there
3696
3697 mark_comprehension_set_with(b(X,Type,I),ANN,b(X2,Type,I2)) :- % ANN = 'SYMBOLIC' or 'FORCE' or ...
3698 (X=comprehension_set(Ids,b(P,pred,IP))
3699 -> X2 = comprehension_set(Ids,b(P,pred,IP2)), I2=I,
3700 add_info_if_new(IP,prob_annotation(ANN),IP2)
3701 ; X2=X, add_info_if_new(I,prob_annotation(ANN),I2)
3702 ).
3703
3704 % pred(h) --> h-1, succ(h) --> h+1
3705 precompute_pred_succ_function_call(Fun,Arg,ArithOp) :-
3706 get_texpr_expr(Fun,PS),
3707 ( PS=predecessor -> Op=minus
3708 ; PS=successor -> Op=add), % we could use add_one predicate
3709 ArithOp =.. [Op,Arg,Integer],
3710 create_texpr(integer(1),integer,[],Integer).
3711
3712 %
3713 one(b(integer(1),integer,[])).
3714 create_interval_member(X,LowBound,UpBound,Member) :-
3715 safe_create_texpr(interval(LowBound,UpBound),set(integer),Interval),
3716 safe_create_texpr(member(X,Interval),pred,Member).
3717
3718 get_leq_comparison(less(A,B),A,B1) :- minus_one(B,BM1),
3719 safe_create_texpr(BM1,integer,B1).
3720 get_leq_comparison(greater(B,A),A,B1) :- get_leq_comparison(less(A,B),A,B1).
3721 get_leq_comparison(less_equal(A,B),A,B).
3722 get_leq_comparison(greater_equal(B,A),A,B).
3723
3724 minus_one(b(integer(I),integer,_),Res) :- !, I1 is I-1, Res=integer(I1).
3725 minus_one(B,minus(B,One)) :- one(One).
3726 add_one(b(integer(I),integer,_),Res) :- !, I1 is I+1, Res=integer(I1).
3727 add_one(B,add(B,One)) :- one(One).
3728
3729 % get_geq_comparison(Expr,LHS,RHS) ; RHS can be shifted by 1
3730 get_geq_comparison(less(B,A),A,B1) :- get_geq_comparison(greater(A,B),A,B1).
3731 get_geq_comparison(greater(A,B),A,B1) :- add_one(B,BP1),
3732 safe_create_texpr(BP1,integer,B1).
3733 get_geq_comparison(less_equal(B,A),A,B).
3734 get_geq_comparison(greater_equal(A,B),A,B).
3735 get_geq_comparison(member(A,SET),A,b(integer(Bound),integer,[])) :-
3736 is_inf_integer_set_with_lower_bound(SET,Bound).
3737 % comparison operators:
3738 comparison(equal(A,B),A,B,equal(SA,SB),SA,SB).
3739 comparison(not_equal(A,B),A,B,not_equal(SA,SB),SA,SB).
3740 comparison(greater(A,B),A,B,greater(SA,SB),SA,SB).
3741 comparison(less(A,B),A,B,less(SA,SB),SA,SB).
3742 comparison(greater_equal(A,B),A,B,greater_equal(SA,SB),SA,SB).
3743 comparison(less_equal(A,B),A,B,less_equal(SA,SB),SA,SB).
3744 % rules to simplify binary comparison arguments
3745 simplify_comparison_terms(b(A,T,_IA),b(B,T,_IB),RA,RB) :-
3746 simplify_comparison_terms2(A,B,RA,RB).
3747 % TO DO: expand into much better simplifier !
3748 simplify_comparison_terms2(minus(A1,A2),minus(B1,B2),ResA,ResB) :-
3749 ( same_texpr(A1,B1) -> ResA=B2, ResB=A2 % X-A2 < X-B2 <=> B2 < A2
3750 ; same_texpr(A2,B2) -> ResA=A1, ResB=B1). % A1-X < B1-X <=> A1 < B1
3751 simplify_comparison_terms2(add(A1,A2),add(B1,B2),ResA,ResB) :-
3752 ( same_texpr(A1,B1) -> ResA=A2, ResB=B2 % X+A2 < X+B2 <=> A2 < B2
3753 ; same_texpr(A2,B2) -> ResA=A1, ResB=B1 % A1+X < B1+X <=> A1 < B1
3754 ; same_texpr(A1,B2) -> ResA=A2, ResB=B1 % X+A2 < B1+X <=> A2 < B1
3755 ; same_texpr(A2,B1) -> ResA=A1, ResB=B2). % A1+X < X+B2 <=> A1 < B2
3756 % multiplication: beware of sign, same with division
3757
3758
3759
3760 % simplify equality/inequality Unifications:
3761 simplify_equality(b(A,T,_),b(B,T,_),A2,B2) :-
3762 simplify_equality_aux(A,B,T,A2,B2).
3763 simplify_equality_aux(set_extension([A1]),set_extension([B1]),_,A2,B2) :- !,
3764 opt_simplify_equality(A1,B1,A2,B2).
3765 simplify_equality_aux(CoupleA,Type,CoupleB,A3,B3) :-
3766 get_couple(CoupleA,Type,A1,A2),
3767 get_couple(CoupleB,Type,B1,B2),
3768 (same_texpr(A1,B1), % (1,x)=(1,3) -> x=3
3769 always_well_defined_or_disprover_mode(A1)
3770 -> opt_simplify_equality(A2,B2,A3,B3)
3771 ; same_texpr(A2,B2),always_well_defined_or_disprover_mode(A2)
3772 -> opt_simplify_equality(A1,B1,A3,B3)
3773 ; different_texpr_values(A1,B1), % (1,x)=(2,3) -> 1=2
3774 always_well_defined_or_wd_improvements_allowed(A2),
3775 always_well_defined_or_wd_improvements_allowed(B2)
3776 -> (A3,B3) = (A1,B1) % no need to compare A2,B2
3777 ; different_texpr_values(A2,B2), always_well_defined_or_wd_improvements_allowed(A1),
3778 always_well_defined_or_wd_improvements_allowed(B1)
3779 -> (A3,B3) = (A2,B2) % no need to compare A1,B1
3780 ).
3781
3782 % flexibly get a couple from either AST or value:
3783 get_couple(couple(A1,A2),_,A1,A2).
3784 get_couple(value(CVal),couple(T1,T2),A1,A2) :- nonvar(CVal), CVal=(V1,V2),
3785 A1=b(value(V1),T1,[]), A2=b(value(V2),T2,[]).
3786
3787 opt_simplify_equality(A1,B1,A2,B2) :-
3788 (simplify_equality(A1,B1,A2,B2) -> true ; A2=A1, B2=B1).
3789
3790 % record field set extraction:
3791 construct_field_sets(FieldsSetsOut, field(Name,Type), field(Name,NewSet)) :-
3792 (member(field_set(Name,NewSet),FieldsSetsOut)
3793 -> true
3794 ; create_maximal_type_set(Type,NewSet)
3795 ).
3796
3797 % traverse a list of conjuncts and check that they all restrict fields of a record ID
3798 % all sets are combined via intersection
3799 l_update_record_field_membership([],_) --> [].
3800 l_update_record_field_membership([H|T],ID) -->
3801 update_record_field_membership(H,ID), l_update_record_field_membership(T,ID).
3802 update_record_field_membership(b(member(b(LHS,_,_),TRHS),pred,_),ID) --> update2(LHS,TRHS,ID).
3803 update2(record_field(RECID,FieldName),TRHS,ID) --> {get_texpr_id(RECID,ID)},add_field_restriction(FieldName,TRHS).
3804 update2(identifier(ID),b(struct(b(rec(FieldSets),_,_)),_,_),ID) -->
3805 l_add_field_restriction(FieldSets).
3806
3807 l_add_field_restriction([]) --> [].
3808 l_add_field_restriction([field(FieldName,TRHS)|T]) -->
3809 add_field_restriction(FieldName,TRHS), l_add_field_restriction(T).
3810 add_field_restriction(FieldName,TRHS,FieldsIn,FieldsOut) :-
3811 (select(field_set(FieldName,OldSet),FieldsIn,F2)
3812 -> OldSet = b(_,Type,_),
3813 safe_create_texpr(intersection(OldSet,TRHS),Type,NewSet),
3814 FieldsOut = [field_set(FieldName,NewSet)|F2]
3815 ; FieldsOut = [field_set(FieldName,TRHS)|FieldsIn]).
3816 % end record field extraction
3817
3818
3819 extract_unions(A,R) :- get_texpr_expr(A,union(A1,A2)),!,
3820 extract_unions(A1,R1), extract_unions(A2,R2),
3821 append(R1,R2,R).
3822 extract_unions(A,[A]).
3823
3824 gen_member_predicates(B,SEl,TExpr) :- safe_create_texpr(member(SEl,B),pred,TExpr).
3825
3826 % when constructing an expression/predicate: important to ripple wd information up
3827 extract_important_info_from_subexpression(b(_,_,Info),NewInfo) :-
3828 include(important_info_from_sub_expr,Info,NewInfo).
3829
3830 extract_important_info_from_subexpressions(b(_,_,Info1),b(_,_,Info2),NewInfo) :-
3831 include(important_info_from_sub_expr,Info1,II1),
3832 (memberchk(contains_wd_condition,Info2), nonmember(contains_wd_condition,II1)
3833 -> NewInfo = [contains_wd_condition|II1]
3834 ; NewInfo=II1). % TO DO: maybe also import other Infos? merge position info (nodeid(_))?
3835 % other important ones: removed_typing ??
3836
3837 % include important info from removed conjunct (note: see also extract_info in bsyntaxtree)
3838 % include_important_info(RemovedPredInfo,RemainingPredInfo,NewInfo)
3839 include_important_info_from_removed_pred([],Info2,Info2).
3840 include_important_info_from_removed_pred([H1|Info1],Info2,NewInfo2) :-
3841 (is_removed_typing_info(H1), nonmember(removed_typing,Info2)
3842 -> NewInfo2 = [removed_typing|Info2]
3843 ; include_important_info_from_removed_pred(Info1,Info2,NewInfo2)).
3844
3845 is_removed_typing_info(was(_)).
3846 is_removed_typing_info(removed_typing).
3847
3848 important_info_from_sub_expr(removed_typing).
3849 important_info_from_sub_expr(contains_wd_condition).
3850 important_info_from_sub_expr(prob_annotation(_)).
3851 important_info_from_sub_expr(nodeid(_)).
3852 important_info_from_sub_expr(was(_)).
3853 %important_info(allow_to_lift_exists). % important but only for exists; should not be copied to outer predicates
3854
3855 % add important infos in case an expression gets simplified into a sub-expression, e.g., bool(X)=TRUE -> X
3856 add_important_info_from_super_expression(Infos,SubInfos,NewSubInfos) :-
3857 include(important_info_from_super_expression,Infos,Important), % print(add_important(Important)),nl,
3858 add_infos_if_new(Important,SubInfos,NewSubInfos).
3859
3860
3861 important_info_from_super_expression(label(_)).
3862 important_info_from_super_expression(P) :- is_rodin_label_info(P). % important for Rodin proof info; if an invariant is partioned
3863 %add_important_info_to_texpr_from_super(Infos,b(Sub,T,SubInfos),b(Sub,T,NewSubInfos)) :- !,
3864 % add_important_info_from_super_expression(Infos,SubInfos,NewSubInfos).
3865
3866 important_info_for_exists(allow_to_lift_exists).
3867 important_info_for_exists(Label) :- important_info_from_super_expression(Label).
3868 % add important infos to individual conjuncts which are exists constructs; used when partitioning of exists:
3869 add_important_infos_to_exists_conjuncts(TPred,SuperInfos,SuperIds,NewTPred) :-
3870 include(important_info_for_exists,SuperInfos,Important),
3871 (Important=[]
3872 -> NewTPred=TPred
3873 ; add_to_conjuncts_aux(TPred,Important,SuperIds,NewTPred)
3874 ).
3875 add_to_conjuncts_aux(b(Pred,Type,Infos),Important,SuperIds,b(NP,Type,NewSubInfos)) :-
3876 (Pred=conjunct(A,B)
3877 -> NP=conjunct(NA,NB), NewSubInfos=Infos,
3878 add_to_conjuncts_aux(A,Important,SuperIds,NA),
3879 add_to_conjuncts_aux(B,Important,SuperIds,NB)
3880 ? ; Pred=exists(InnerIds,_), member(TID,InnerIds),
3881 def_get_texpr_id(TID,ID),
3882 ord_member(ID,SuperIds) % the exists is probably related to the outer exists
3883 % (TODO: it could be a lifted clashing one: perform this annotation directly in construct_optimized_exists !!)
3884 -> append(Important,Infos,NewSubInfos), % copy infos from original exists
3885 NP=Pred
3886 ; NP=Pred, NewSubInfos=Infos).
3887
3888 % extract all assignments from a list of statements; last arg is the number of assignments extracted
3889 extract_assignments([],[],[],[],0).
3890 extract_assignments([H|T],ResLHS,ResRHS,Rest,Nr) :-
3891 is_ordinary_assignment(H,LHS,RHS,Cnt),!,
3892 extract_assignments(T,LT,RT,Rest,N),
3893 append(LHS,LT,ResLHS), append(RHS,RT,ResRHS), Nr is N+Cnt.
3894 extract_assignments([H|T],LT,RT,[H|Rest],Nr) :-
3895 extract_assignments(T,LT,RT,Rest,Nr).
3896 % assigned_after(Primed),modifies(Var)
3897
3898 % check if we have an ordinary assignment that can be merged, optimised:
3899 is_ordinary_assignment(b(S,_,Info),LHS,RHS,Cnt) :- is_ordinary_assignment_aux(S,Info,LHS,RHS,Cnt).
3900 is_ordinary_assignment_aux(skip,_Info,[],[],0).
3901 is_ordinary_assignment_aux(assign_single_id(LHS,RHS),_Info,[LHS],[RHS],0). % count=0: used for parallel merge: assign_single_id less useful to merge with
3902 is_ordinary_assignment_aux(assign(LHS,RHS),Info,LHS,RHS,1) :-
3903 \+ member(assigned_after(_),Info), % these assignments are treated in a special way
3904 \+ member(modifies(_),Info).
3905
3906 % merge assignments in a list of statements to be executed by sequential composition:
3907 merge_assignments([S1,S2|T],merged,Res) :- merge_two_assignments(S1,S2,New),!,
3908 merge_assignments([New|T],_,Res).
3909 merge_assignments([],no_merge,[]).
3910 merge_assignments([H|T],Merge,[H|TR]) :- merge_assignments(T,Merge,TR).
3911
3912 :- use_module(b_read_write_info,[get_lhs_assigned_identifier/2]).
3913 get_lhs_assigned_ids(LHS,SortedIds) :-
3914 maplist(get_lhs_assigned_identifier,LHS,TLHSAssign),
3915 get_sorted_ids(TLHSAssign,SortedIds).
3916 merge_two_assignments(S1,S2,NewAssignment) :-
3917 is_ordinary_assignment(S1,LHS1,RHS1,_),
3918 is_ordinary_assignment(S2,LHS2,RHS2,_),
3919 get_lhs_assigned_ids(LHS1,SortedIDs1),
3920 get_lhs_assigned_ids(LHS2,SortedIDs2),
3921 ord_disjoint(SortedIDs1,SortedIDs2), % no race condition
3922 maplist(not_occurs_in_predicate(SortedIDs1),RHS2),
3923 maplist(not_occurs_in_predicate(SortedIDs1),LHS2), % not used in left-hand side, e.g., f(x+y) := RHS
3924 get_texpr_info(S1,I1), get_texpr_info(S2,I2),
3925 merge_info(I1,I2,Infos),
3926 append(LHS1,LHS2,NewLHS),
3927 append(RHS1,RHS2,NewRHS),
3928 NewAssignment = b(assign(NewLHS,NewRHS),subst,Infos),
3929 (debug_mode(off) -> true ; print('Merged assignments: '),translate:print_subst(NewAssignment),nl).
3930 construct_sequence([],skip).
3931 construct_sequence([TH],H) :- !, TH=b(H,_,_).
3932 construct_sequence(List,sequence(List)).
3933
3934 % check if we have a simple expression which will not be complicated to calculate
3935 ?simple_expression(b(E,_,_)) :- simple2(E).
3936 % (simple2(E) -> true ; print(not_simple(E)),nl).
3937 simple2(bool_set).
3938 simple2(boolean_false).
3939 simple2(boolean_true).
3940 simple2(empty_sequence).
3941 simple2(empty_set).
3942 simple2(identifier(_)).
3943 %simple2(integer_set(_)).
3944 simple2(integer(_)).
3945 simple2(lazy_lookup_expr(_)).
3946 simple2(lazy_let_expr(_,A,B)) :- simple2(B), simple_expr_or_pred(A).
3947 simple2(max_int).
3948 simple2(min_int).
3949 simple2(real_set).
3950 simple2(real(_)).
3951 simple2(string_set).
3952 simple2(string(_)).
3953 simple2(value(_)).
3954 simple2(first_of_pair(_)). simple2(second_of_pair(_)).
3955 simple2(couple(A,B)) :- simple_expression(A), simple_expression(B).
3956 ?simple2(interval(A,B)) :- simple_expression(A), simple_expression(B).
3957 ?simple2(add(A,B)) :- simple_expression(A), simple_expression(B).
3958 ?simple2(minus(A,B)) :- simple_expression(A), simple_expression(B).
3959 simple2(multiplication(A,B)) :- simple_expression(A), simple_expression(B).
3960 simple2(unary_minus(A)) :- simple_expression(A).
3961 ?simple2(convert_bool(A)) :- simple_predicate(A).
3962 simple2(sequence_extension(A)) :- maplist(simple_expression,A).
3963 simple2(set_extension(A)) :- maplist(simple_expression,A). % a bit more expensive than sequence_extension: elements need to be compared
3964 simple2(X) :- is_integer_set(X,_).
3965
3966 simple_expr_or_pred(b(E,T,_)) :- (T=pred -> simplep2(E) ; simple2(E)).
3967
3968 ?simple_predicate(b(E,_,_)) :- simplep2(E).
3969 ?simplep2(equal(A,B)) :- simple_expression(A), simple_expression(B). % could be slightly more expensive if set type
3970 simplep2(not_equal(A,B)) :- simple_expression(A), simple_expression(B). % ditto
3971 simplep2(lazy_let_pred(_,A,B)) :- simple_predicate(B), simple_expr_or_pred(A).
3972 simplep2(less(A,B)) :- simple_expression(A), simple_expression(B).
3973 simplep2(less_equal(A,B)) :- simple_expression(A), simple_expression(B).
3974 simplep2(greater(A,B)) :- simple_expression(A), simple_expression(B).
3975 simplep2(greater_equal(A,B)) :- simple_expression(A), simple_expression(B).
3976
3977 % detect ID = Expr or Expr = ID
3978 identifier_equality(TExpr,ID,TID,Expr) :- is_equality(TExpr,LHS,RHS),
3979 ( get_texpr_id(LHS,ID), TID=LHS, Expr = RHS
3980 ; get_texpr_id(RHS,ID), TID=RHS, Expr = LHS).
3981
3982
3983 ?id_member_of_set_extension(TExpr,ID,TID,[Expr]) :- identifier_equality(TExpr,ID,TID,Expr).
3984 id_member_of_set_extension(b(member(TID,SEXT),pred,_),ID,TID,SList) :- get_texpr_id(TID,ID),
3985 is_set_extension(SEXT,SList).
3986
3987 % check if merging disjunctions of set_extensionts of this type is useful in CLP(FD) mode
3988 % see get_fd_type in clpfd_tables, ...
3989 type_contains_fd_index(V) :- var(V),!,fail.
3990 type_contains_fd_index(couple(A,_)) :- type_contains_fd_index(A).
3991 type_contains_fd_index(record([field(_,T1)|_])) :- type_contains_fd_index(T1).
3992 type_contains_fd_index(global(_)).
3993 type_contains_fd_index(integer).
3994 type_contains_fd_index(boolean).
3995
3996
3997 create_equalities_for_let(ORefs,Primed,Equalities) :-
3998 maplist(create_equality_for_let,ORefs,Primed,Equalities).
3999 create_equality_for_let(oref(PrimedId,OrigId,Type),TPrimed,Equality) :-
4000 create_texpr(identifier(OrigId),Type,[],TOrig),
4001 create_texpr(identifier(PrimedId),Type,[],TPrimed),
4002 safe_create_texpr(equal(TPrimed,TOrig),pred,Equality).
4003
4004 % inserts a let statement. If the original statement is a precondition or any, the let is moved
4005 % inside the original statement to prevent strange side-effects. This can be used for other,
4006 % non-value changing substitutions as well.
4007 insert_let(TExpr,Ids,P,NTExpr) :-
4008 remove_bt(TExpr,Expr,NewExpr,NTExpr),
4009 move_let_inside(Expr,Old,New,NewExpr),!,
4010 insert_let(Old,Ids,P,New).
4011 insert_let(TExpr,Ids,P,NTExpr) :-
4012 create_texpr(let(Ids,P,TExpr),subst,[],NTExpr).
4013 move_let_inside(precondition(Cond,Old),Old,New,precondition(Cond,New)).
4014 move_let_inside(any(Any,Where,Old),Old,New,any(Any,Where,New)).
4015
4016 % find_one_point_rules(+TypedIds,+Predicates,+Blacklist,
4017 % -LetIds,-LetExprs,-RemainingIds,-RemainingPredicates)
4018 % TypedIds: The ids that are quantified in the exists clause
4019 % Predicates: The predicates of the exists (without already used id=E predicates)
4020 % Blacklist: All ids that must not be used in the found expression
4021 % LetIds: The ids that can be introduced as LET
4022 % LetExprs. For each id (in LetIds) the corresponding expression
4023 % RemainingIds: The ids that are not converted into LETs
4024 % RemainingPredicates: The predicates after removing the id=E predicates
4025 % (e.g., f = {1|->2} & !e.(2:dom(f) & e=f(2) => e>100) should not generate a WD-error)
4026 find_one_point_rules(TIds,Preds,Blacklist,LetIds,Exprs,RestIds,NewPreds) :-
4027 typed_ids_to_avl(TIds,AVL),
4028 find_one_point_equalities(Preds,is_leftmost,AVL,Blacklist,LetIds,Exprs,RestIds,NewPreds).
4029
4030 :- use_module(library(avl),[avl_range/2, avl_fetch/3, avl_delete/4, empty_avl/1]).
4031 :- use_module(bsyntaxtree,[is_equality/3]).
4032 find_one_point_equalities([],_,AVL,_,[],[],RestIds,[]) :-
4033 avl_range(AVL,RestIds).
4034 find_one_point_equalities([TEqual|TPreds],WDLEFT,AVL,Blacklist,LetIds,Exprs,RestIds,NewPreds) :-
4035 split_equality(TEqual,TEq1,TEq2),
4036 %print('Splitting equality: '), translate:print_bexpr(TEqual),nl,
4037 !,
4038 find_one_point_equalities([TEq1,TEq2|TPreds],WDLEFT,AVL,Blacklist,LetIds,Exprs,RestIds,NewPreds).
4039 find_one_point_equalities([TEqual|TPreds],WDLEFT,AVL,Blacklist,[LetID|LetIds],[Expr|Exprs],RestIds,NewPreds) :-
4040 is_equality(TEqual,TA,TB),
4041 ( get_texpr_id(TA,Id),TB=Expr ; get_texpr_id(TB,Id),TA=Expr ),
4042 avl_fetch(Id,AVL,LetID), % we have an equality involving a quantified identifier
4043 %print(wdleft(WDLEFT,Id)),nl, print_bexpr(Expr),nl,
4044 (WDLEFT=is_leftmost
4045 -> true % all the expressions to the left of the equality are well-defined
4046 ; always_defined_full_check_or_disprover_mode(Expr)), % we can lift Id=Expr to an outer LET
4047 % example: UNION(x).(1:dom(f) & x=f(1)|{x}) --> do not lift x=f(1) out if f(1) is not WD, see test 2195
4048 find_identifier_uses_if_necessary(Expr,[],UsedIds),
4049 ord_disjoint(Blacklist,UsedIds),
4050 % TODO: what if we have an equality between two quantified ids in the blacklist?
4051 !,
4052 avl_delete(Id,AVL,_,AVL2),
4053 (empty_avl(AVL2) % check if we have found equalities for all ids
4054 -> NewPreds=TPreds, LetIds=[],Exprs=[],RestIds=[]
4055 ; % should we add Id to Blacklist; usually all ids are already in the blacklist
4056 % WDLEFT remains unchanged
4057 update_wd_to_left(WDLEFT,Expr,NewWDLEFT),
4058 find_one_point_equalities(TPreds,NewWDLEFT,AVL2,Blacklist,LetIds,Exprs,RestIds,NewPreds)
4059 ).
4060 find_one_point_equalities([TEqual|TPreds],_WDLEFT,AVL,Blacklist,LetIds,Exprs,RestIds,[TEqual|NewPreds]) :-
4061 %update_wd_to_left(WDLEFT,Expr,NewWDLEFT),
4062 find_one_point_equalities(TPreds,not_leftmost,AVL,Blacklist,LetIds,Exprs,RestIds,NewPreds).
4063
4064 % update information about whether the next conjunct should still be considered as leftmost concerning WD
4065 update_wd_to_left(is_leftmost,Expr,NewWD) :- always_defined_full_check_or_disprover_mode(Expr),!,
4066 NewWD = is_leftmost.
4067 update_wd_to_left(_,_,not_leftmost).
4068
4069
4070 :- use_module(library(avl),[list_to_avl/2]).
4071 get_avl_aux(TID,Id-TID) :- get_texpr_id(TID,Id).
4072
4073 % convert an unsorted typed identifier list to an AVL tree
4074 typed_ids_to_avl(TIds,AVL) :-
4075 maplist(get_avl_aux,TIds,L),
4076 list_to_avl(L,AVL).
4077
4078
4079 % select a predicate from Preds of the form id=Expr (or Expr=id) where Expr does not contain
4080 % references to identifiers in Blacklist. Rest is Preds without id=Expr
4081 select_equality(TId,Preds,Blacklist,TEqual,Expr,Rest,UsedIds,CheckWellDef) :-
4082 get_texpr_id(TId,Id),
4083 ? safe_select(CheckWellDef,TEqual,Preds,Rest),
4084 is_equality(TEqual,TA,TB), % the ast_cleanup rules have not run yet on the sub-expressions of the exists: detect more equalities
4085 ( get_texpr_id(TA,Id),TB=Expr ; get_texpr_id(TB,Id),TA=Expr ),
4086 find_identifier_uses_if_necessary(Expr,[],UsedIds),
4087 ord_disjoint(Blacklist,UsedIds).
4088
4089
4090 % safely select a predicate from List, preserving WD (well-definedness)
4091 safe_select(check_well_definedness,Element,[H|T],Rest) :- !,
4092 (Element=H,Rest=T % either first element
4093 ; % or if later element; then it must be well-defined; otherwise H could fail
4094 ? select(Element,T,TRest), Rest=[H|TRest],
4095 always_defined_full_check_or_disprover_mode(Element) % we cannot use always_well_defined(Element) in cleanup_pre; it is only computed in cleanup_post at the moment; TO DO: we now do compute in pre phase
4096 %, print(always_wd(Element)),nl
4097 ).
4098 ?safe_select(_,Element,List,Rest) :- select(Element,List,Rest).
4099
4100 % split predicate list into conjuncts using a certain list of ids and those not
4101 split_predicates(LP,Ids,UsingIds,NotUsingIds) :-
4102 get_sorted_ids(Ids,SIds),
4103 ? filter(not_occurs_in_predicate(SIds),LP,NP,UP),
4104 conjunct_predicates_with_pos_info(NP,NotUsingIds),
4105 conjunct_predicates_with_pos_info(UP,UsingIds).
4106
4107 not_occurs_in_predicate([],_Pred) :- !.
4108 not_occurs_in_predicate(SortedIDs,Pred) :- SortedIDs = [ID1|_],
4109 (ID1 = b(_,_,_) -> add_internal_error('Wrapped identifiers: ',not_occurs_in_predicate(SortedIDs,Pred)) ; true),
4110 find_identifier_uses_if_necessary(Pred,[],UsedIds),
4111 ord_disjoint(SortedIDs,UsedIds).
4112 get_sorted_ids(Ids,SIds) :-
4113 get_texpr_ids(Ids,UnsortedIds),sort(UnsortedIds,SIds).
4114
4115 % see also is_succ_compset, is_pred_compset
4116 % create directly value rather than comprehension_set([A,B],Pred), see test 2560 for detecting infinite closures
4117 pred_succ_compset(Op,value(closure([AId,BId],[integer,integer],Pred))) :-
4118 %get_unique_id('_a_',AId),get_unique_id('_b_',BId),
4119 BId = '_lambda_result_',
4120 (Op = add -> AId = '_succ_' ; AId='_pred_'),
4121 create_texpr(identifier(AId),integer,[],A),
4122 create_texpr(identifier(BId),integer,[lambda_result(BId)],B),
4123 create_texpr(integer(1),integer,[],Integer),
4124 create_texpr(ArithOp,integer,[],TArithOp),
4125 ArithOp =.. [Op,A,Integer],
4126 safe_create_texpr(equal(B,TArithOp),pred,[prob_annotation('LAMBDA-EQUALITY')],Pred).
4127
4128 add_used_identifier_info(_Ids,_P,IOld,INew) :-
4129 ? member(used_ids(_),IOld),!,INew=IOld.
4130 add_used_identifier_info(Ids,P,IOld,[used_ids(FoundIds)|IOld]) :-
4131 % add used identifiers to information
4132 get_sorted_ids(Ids,Ignore),
4133 find_identifier_uses(P,Ignore,FoundIds).
4134
4135 add_used_ids_defined_by_equality(_Ids,P,IOld,INew) :-
4136 ? member(used_ids(UsedIds),IOld),!,
4137 findall(ID, (member_in_conjunction(Equality,P),
4138 identifier_equality(Equality,ID,_TID,Expr),
4139 ord_member(ID,UsedIds),
4140 not_occurs_in_expr(ID,Expr)), % TO DO: we could check also other ids defined by equality already found
4141 IdsDefinedByEquality),
4142 ? (select(used_ids_defined_by_equality(_),IOld,I1) -> true ; I1=IOld),
4143 (IdsDefinedByEquality=[] -> INew=I1
4144 ; %debug_println(9,used_ids_defined_by_equality(IdsDefinedByEquality)),
4145 INew = [used_ids_defined_by_equality(IdsDefinedByEquality)|I1]
4146 ).
4147 add_used_ids_defined_by_equality(Ids,P,IOld,INew) :-
4148 add_internal_error('No used_ids info:',add_used_ids_defined_by_equality(Ids,P,IOld,INew)),
4149 INew=IOld.
4150
4151
4152 % can be used to check the validity of the used_ids field, e.g., for existential quantifier
4153 check_used_ids_info(Parameters,Predicate,StoredUsedIds,PP) :-
4154 % get_global_identifiers(Ignored), and add to Parameters ??
4155 (add_used_identifier_info(Parameters,Predicate,[],[used_ids(UsedIds)])
4156 -> (StoredUsedIds=UsedIds -> true
4157 ; ord_subset(UsedIds,StoredUsedIds)
4158 -> format('Suboptimal used_ids info: ~w (actual ~w) [origin ~w with ~w]~n',[StoredUsedIds,UsedIds,PP,Parameters])
4159 %, print_bexpr(Predicate),nl
4160 ; add_internal_error('Incorrect used_ids info: ', check_used_ids_info(Parameters,Predicate,UsedIds,PP)),
4161 print_bexpr(Predicate),nl,
4162 (extract_span_description(Predicate,PosMsg) -> format('Location: ~w~n',[PosMsg]) ; true),
4163 ord_subtract(UsedIds,StoredUsedIds,Delta),
4164 format('Incorrect used_ids info: ~w (actual ~w)~nNot included: ~w~n~n',[StoredUsedIds,UsedIds,Delta])
4165 %, print_bexpr(Predicate),nl
4166 )
4167 ; add_internal_error('Could not computed used ids:',
4168 add_used_identifier_info(Parameters,Predicate,[],[used_ids(_)]))
4169 ).
4170
4171 % update used_ids_info for existential and universal quantifier (at top-level only !)
4172 recompute_used_ids_info(b(E,pred,Info0),Res) :-
4173 delete(Info0,used_ids(_),Info1),
4174 recompute_used_ids_info_aux(E,Info1,Info2),!, Res= b(E,pred,Info2).
4175 recompute_used_ids_info(TE,TE).
4176
4177 compute_used_ids_info_if_necessary(b(E,pred,Info1),Res) :-
4178 recompute_used_ids_info_aux(E,Info1,Info2),!, Res= b(E,pred,Info2).
4179 compute_used_ids_info_if_necessary(TE,TE).
4180
4181 recompute_used_ids_info_aux(exists(Parameters,Predicate),Info1,Info2) :-
4182 add_used_identifier_info(Parameters,Predicate,Info1,Info2).
4183 recompute_used_ids_info_aux(forall(Parameters,Lhs,Rhs),Info1,Info2) :-
4184 conjunct_predicates([Lhs,Rhs],Predicate),
4185 add_used_identifier_info(Parameters,Predicate,Info1,Info2).
4186 % for while loop we could recompute modifies and reads info
4187
4188 % generation of unique identifiers
4189 :- dynamic unique_id_counter/1.
4190 unique_id_counter(1).
4191
4192 get_unique_id_inside(Prefix,Pred,ResultId) :-
4193 (\+ occurs_in_expr(Prefix,Pred) % first try and see whether we need to append a number
4194 -> ResultId = Prefix
4195 ; get_unique_id(Prefix,ResultId)
4196 ).
4197 get_unique_id_inside(Prefix,Pred,Expr,ResultId) :-
4198 ( \+ occurs_in_expr(Prefix,Pred),
4199 \+ occurs_in_expr(Prefix,Expr) % first try and see whether we need to append a number
4200 -> ResultId = Prefix
4201 ; get_unique_id(Prefix,ResultId)
4202 ).
4203
4204 get_unique_id(Prefix,Id) :-
4205 retract(unique_id_counter(Old)),
4206 New is Old + 1,
4207 assertz(unique_id_counter(New)),
4208 safe_atom_chars(Prefix,CPrefix,get_unique_id1),
4209 number_chars(Old,CNumber),
4210 append(CPrefix,CNumber,CId),
4211 safe_atom_chars(Id,CId,get_unique_id2).
4212
4213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4214 % replace all variables in types by "any"
4215
4216 ground_type_to_any(T,Exceptions) :- var(T),!,
4217 ( exact_member(T,Exceptions) -> ! % any variable exact member in list is not grounded
4218 ; T=any).
4219 ground_type_to_any(record(Fields),Exceptions) :- !,
4220 % treat records separately, we do not want the list of fields to be bound to any; see B/Tickets/RecordPartiallyTyped
4221 ground_field_types(Fields,Exceptions).
4222 ground_type_to_any(seq(T),Exceptions) :- !, ground_type_to_any(T,Exceptions).
4223 ground_type_to_any(set(T),Exceptions) :- !, ground_type_to_any(T,Exceptions).
4224 ground_type_to_any(couple(A,B),Exceptions) :- !, ground_type_to_any(A,Exceptions), ground_type_to_any(B,Exceptions).
4225 ground_type_to_any(global(_),_) :- !.
4226 ground_type_to_any(freetype(_),_) :- !.
4227 ground_type_to_any(T,_) :- ground(T),!.
4228 ground_type_to_any(T,Exceptions) :-
4229 functor(T,_,Arity),
4230 ground_type_args(Arity,T,Exceptions).
4231 ground_type_args(0,_T,_Exceptions) :- !.
4232 ground_type_args(N,T,Exceptions) :-
4233 arg(N,T,Arg),
4234 ground_type_to_any(Arg,Exceptions),
4235 N2 is N-1,
4236 ground_type_args(N2,T,Exceptions).
4237
4238 ground_field_types(T,Exceptions) :- var(T),!,
4239 ( exact_member(T,Exceptions) -> ! % any variable exact member in list is not grounded
4240 ; print(grounding_open_ended_record),nl, % should we generate a warning here ?
4241 % Note: did happen for type checking definitions; e.g., via bvisual2:register_top_level(definitions)
4242 % now we pass do_not_ground_types for this
4243 T=[]
4244 ).
4245 ground_field_types([],_) :- !.
4246 ground_field_types([field(Name,Type)|T],Exceptions) :- !,
4247 (var(Name)
4248 -> add_internal_error('Unbound record field name: ',ground_field_types([field(Name,Type)|T],Exceptions))
4249 ; true),
4250 ground_type_to_any(Type,Exceptions),
4251 ground_field_types(T,Exceptions).
4252 ground_field_types(Other,Exceptions) :-
4253 add_internal_error('Illegal record field list: ',ground_field_types(Other,Exceptions)).
4254
4255 % annote variables of becomes_such with before_substitution infos
4256 annotate_becomes_such_vars(Ids1,Pred,Ids2) :-
4257 find_used_primed_ids(Pred,Ids1,BeforeAfter),
4258 maplist(add_before_after_info(BeforeAfter),Ids1,Ids2).
4259 % put optional before/after usage into the information of the identifiers
4260 % makes only sense in the context of becomes_such substitutions
4261 add_before_after_info(BeforeAfter,TId,TId2) :-
4262 def_get_texpr_id(TId,Id),
4263 ( member(ba(Id,BeforeId),BeforeAfter) ->
4264 get_texpr_type(TId,Type), get_texpr_info(TId,Info),
4265 create_texpr(identifier(Id),Type,[before_substitution(Id,BeforeId)|Info],TId2)
4266 ; TId = TId2 ).
4267
4268 % find all used pairs of before/after variables, e.g. ba(x,'x$0')
4269 % see becomes_such substitutions
4270 find_used_primed_ids(TExpr,PossibleIds,Uses) :-
4271 prime_identifiers0(PossibleIds,TP0),
4272 get_sorted_ids(TP0,SP0), % sorted list of primed ids
4273 find_used_primed_ids2(SP0,TExpr,[],Uses).
4274 find_used_primed_ids2(SP0,TExpr,In,Out) :-
4275 syntaxtraversion(TExpr,Expr,_,_Infos,Subs,_),
4276 ( (Expr = identifier(FullId),
4277 %member(before_substitution(Id,FullId),Infos) % this info is not available in Event-B mode
4278 ord_member(FullId,SP0),
4279 prime_atom0(Id,FullId))
4280 -> ord_add_element(In,ba(Id,FullId),Uses)
4281 ; In = Uses ),
4282 foldl(find_used_primed_ids2(SP0),Subs,Uses,Out).
4283
4284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4285 % find expressions which are possibly not well-defined
4286 is_possibly_undefined(Expr) :- always_wd(Expr),!,fail.
4287 is_possibly_undefined(Expr) :-
4288 safe_syntaxelement_det(Expr,Subs,_Names,_,_),
4289 % note: can be very long list Subs for set_extension
4290 ? member(Sub,Subs),
4291 get_texpr_info(Sub,Info),
4292 memberchk(contains_wd_condition,Info),!.
4293 is_possibly_undefined(Expr) :- % TODO: detect freetype constructors as not having a WD condition
4294 ? has_top_level_wd_condition(Expr). %%,print(wd(F,Expr)),nl.
4295
4296 has_top_level_wd_condition(Expr) :- functor(Expr,F,_),
4297 ? has_wd_condition(F,Expr).
4298
4299 % a few common cases which are always defined:
4300 always_wd(bool_set).
4301 always_wd(boolean_false).
4302 always_wd(boolean_true).
4303 always_wd(empty_set).
4304 always_wd(identifier(_)).
4305 always_wd(integer(_)).
4306 always_wd(integer_set(_)).
4307 always_wd(real_set).
4308 always_wd(real(_)).
4309 always_wd(string(_)).
4310 always_wd(string_set).
4311 %always_wd(value(_)). ?
4312 always_wd(truth).
4313
4314
4315 always_defined_full_check_or_disprover_mode(_) :- preferences:get_preference(disprover_mode,true),!.
4316 always_defined_full_check_or_disprover_mode(BExpr) :- \+ full_check_is_possibly_undefined(BExpr).
4317
4318 % a version which does the full traversal; can be used before contains_wd_condition has been computed
4319 full_check_is_possibly_undefined(BExpr) :- get_texpr_expr(BExpr,Expr),
4320 full_check_is_possibly_undefined_aux(Expr).
4321 full_check_is_possibly_undefined_aux(Expr) :-
4322 functor(Expr,F,_),has_wd_condition(F,Expr),!.
4323 full_check_is_possibly_undefined_aux(Expr) :-
4324 syntaxtraversion(_,Expr,_,_,Subs,_),
4325 ? member(Sub,Subs),
4326 full_check_is_possibly_undefined(Sub),!.
4327
4328 :- use_module(external_functions,[external_fun_has_wd_condition/1]).
4329 % determine if an operator has an attached WD condition (used to compute contains_wd_condition)
4330 % division and module must not divide by zero
4331 has_wd_condition(div,Expr) :- arg(2,Expr,DIV),
4332 ? \+ definitely_not_zero(DIV).
4333 has_wd_condition(floored_div,Expr) :- arg(2,Expr,DIV),
4334 ? \+ definitely_not_zero(DIV).
4335 has_wd_condition(div_real,Expr) :- arg(2,Expr,DIV),
4336 \+ definitely_not_zero(DIV).
4337 % functions must not be applied to values outside their domain
4338 has_wd_condition(function,_Expr). % if lambda over total domain we could remove wd_condition, but ast_cleanup rule then already replaces it with assertion_expression, which gets optimised away in this case
4339 has_wd_condition(assertion_expression,_). % gets translated from function application
4340 has_wd_condition(modulo,Expr) :- arg(2,Expr,DIV),
4341 ? (\+ definitely_not_zero(DIV) ;
4342 \+ definitely_not_negative(DIV) ;
4343 arg(1,Expr,A1), \+ definitely_not_negative(A1)).
4344 % power_of must have a non-negative exponent (?)
4345 has_wd_condition(power_of,Expr) :- arg(2,Expr,EXP),
4346 \+ definitely_not_negative(EXP).
4347 has_wd_condition(power_of_real,Expr) :- arg(1,Expr,Base), arg(2,Expr,EXP),
4348 (definitely_not_negative(Base)
4349 -> (definitely_not_zero(Base) -> fail % no WD if base > 0
4350 ; definitely_not_negative(EXP) -> fail % 0.0 ^ EXP is defined if EXP >=0
4351 ; true % 0.0 ^ -1.0 is a division by zero
4352 )
4353 ; true). % things like -1.0 ^ 0.5 is not a real number; we could check if EXP is guaranteed an integ
4354 % Note: REAL ** INTEGER has no WD condition, but power_of_real has a WD condition
4355 has_wd_condition(iteration,Expr) :- arg(2,Expr,Idx),
4356 \+ definitely_not_negative(Idx).
4357 % min and max need a non-empty set
4358 % also Atelier-B manual requires the set to have an upper limit
4359 has_wd_condition(max,Expr) :- arg(1,Expr,S), \+ definitely_not_empty_and_finite(S).
4360 has_wd_condition(min,Expr) :- arg(1,Expr,S), \+ definitely_not_empty_and_finite(S).
4361 has_wd_condition(max_real,Expr) :- arg(1,Expr,S), \+ definitely_not_empty_and_finite(S).
4362 has_wd_condition(min_real,Expr) :- arg(1,Expr,S), \+ definitely_not_empty_and_finite(S).
4363 % all the sequence operations must not be applied to non-sequence
4364 % relations and some must not be applied to empty-sequences
4365 has_wd_condition(size,E) :- arg(1,E,S), \+ definitely_sequence(S).
4366 has_wd_condition(first,E) :- arg(1,E,S), \+ definitely_not_empty_sequence(S).
4367 has_wd_condition(last,E) :- arg(1,E,S), \+ definitely_not_empty_sequence(S).
4368 has_wd_condition(front,E) :- arg(1,E,S), \+ definitely_not_empty_sequence(S).
4369 has_wd_condition(tail,E) :- arg(1,E,S), \+ definitely_not_empty_sequence(S).
4370 has_wd_condition(rev,E) :- arg(1,E,S), \+ definitely_sequence(S).
4371 has_wd_condition(concat,_).
4372 has_wd_condition(insert_front,_).
4373 has_wd_condition(insert_tail,_).
4374 has_wd_condition(restrict_front,_).
4375 has_wd_condition(restrict_tail,_).
4376 has_wd_condition(general_concat,_).
4377 % the general intersection must not be applied to an empty set of sets
4378 has_wd_condition(general_intersection,Expr) :- arg(1,Expr,S),
4379 \+ definitely_not_empty_set(S).
4380 has_wd_condition(quantified_intersection,_). % gets translated to general_intersection
4381 % card must not be applied to infinite sets:
4382 has_wd_condition(card,Expr) :- arg(1,Expr,S), \+ definitely_finite(S).
4383 has_wd_condition(general_sum,_). % TO DO: \+ definitely_finite2(comprehension_set(Ids,P))
4384 has_wd_condition(general_product,_). % ditto; note: sets which are summed/multiplied must be finite
4385 has_wd_condition(mu,_). % Z MU Operator
4386 has_wd_condition(freetype_destructor,_).
4387 has_wd_condition(external_function_call,Expr) :- arg(1,Expr,FunName),
4388 external_fun_has_wd_condition(FunName).
4389 has_wd_condition(external_pred_call,Expr) :- arg(1,Expr,FunName),
4390 external_fun_has_wd_condition(FunName).
4391 % external_subst_call?
4392 has_wd_condition(operation_call_in_expr,_). % we now assume all operation calls may have PRE-conditions
4393 % or involve recursion, and thus may loop; TO DO: compute this information per operation by a fixpoint algorithm
4394
4395 definitely_not_zero(b(integer(X),integer,_)) :- integer(X), X \= 0.
4396 definitely_not_zero(b(real(X),real,_)) :- atom(X), construct_real(X,R), R \= 0.0.
4397 definitely_not_negative(b(integer(X),integer,_)) :- number(X), X >= 0.
4398 % to do add more ?: card(_), ...
4399
4400
4401 % see definitely_not_empty_set/1 in bsyntaxtree
4402 definitely_not_empty_and_finite(b(S,_,_)) :- def_not_empty_fin2(S).
4403 def_not_empty_fin2(bool_set).
4404 def_not_empty_fin2(set_extension([_|_])).
4405 def_not_empty_fin2(sequence_extension([_|_])).
4406 def_not_empty_fin2(cartesian_product(A,B)) :- definitely_not_empty_and_finite(A), definitely_not_empty_and_finite(B).
4407 def_not_empty_fin2(union(A,B)) :- definitely_not_empty_and_finite(A), definitely_not_empty_and_finite(B).
4408 def_not_empty_fin2(overwrite(A,B)) :- definitely_not_empty_and_finite(A),definitely_not_empty_and_finite(B).
4409 def_not_empty_fin2(interval(A,B)) :- definitely_not_empty_set(b(interval(A,B),set(integer),[])).
4410 def_not_empty_fin2(value(S)) :- % what about closures ?
4411 definitely_not_empty_finite_value(S). %kernel_objects:not_empty_set(S).
4412
4413
4414 definitely_not_empty_sequence(b(S,_,_)) :- definitely_not_empty_sequence2(S).
4415 definitely_not_empty_sequence2(sequence_extension(_)).
4416
4417 definitely_sequence(b(S,_,_)) :- definitely_sequence2(S).
4418 definitely_sequence2(empty_sequence).
4419 definitely_sequence2(sequence_extension(_)).
4420
4421 :- use_module(typing_tools,[is_provably_finite_type/1, is_infinite_type/1]).
4422 is_infinite_ground_type(Type) :-
4423 ground(Type), is_infinite_type(Type). % non-ground types happen e.g. in test 472
4424
4425 definitely_finite(b(S,Type,_)) :-
4426 (ground(Type)
4427 -> (is_provably_finite_type(Type)
4428 -> true
4429 ; definitely_finite2(S) -> true
4430 %; \+ is_infinite_type(Type) -> print(no_longer_assuming_finite(Type)),nl,nl,fail
4431 )
4432 ; definitely_finite2(S)).
4433 definitely_finite2(bool_set).
4434 definitely_finite2(empty_set).
4435 definitely_finite2(empty_sequence).
4436 definitely_finite2(set_extension(_)).
4437 definitely_finite2(sequence_extension(_)).
4438 definitely_finite2(cartesian_product(A,B)) :- definitely_finite(A), definitely_finite(B).
4439 definitely_finite2(overwrite(A,B)) :- definitely_finite(A), definitely_finite(B).
4440 definitely_finite2(union(A,B)) :- definitely_finite(A), definitely_finite(B).
4441 definitely_finite2(intersection(A,B)) :- (definitely_finite(A) -> true ; definitely_finite(B)).
4442 definitely_finite2(set_subtraction(A,_)) :- definitely_finite(A).
4443 definitely_finite2(domain_restriction(_,B)) :- definitely_finite(B). % A finite does not guarantee finite relation
4444 definitely_finite2(domain_subtraction(_,B)) :- definitely_finite(B).
4445 definitely_finite2(range_restriction(A,_)) :- definitely_finite(A). % B finite does not guarantee finite relation
4446 definitely_finite2(range_subtraction(A,_)) :- definitely_finite(A).
4447 definitely_finite2(interval(_,_)).
4448 definitely_finite2(value(S)) :- nonvar(S),(S=[] ; S=avl_set(_)).
4449 % TO DO: add other operators : comprehension_set(Ids,P)
4450
4451
4452 ?definitely_infinite(b(S,_,_)) :- !,definitely_infinite2(S).
4453 definitely_infinite(S) :- add_internal_error('AST not wrapped:',definitely_infinite(S)),fail.
4454 definitely_infinite2(string_set).
4455 definitely_infinite2(real_set).
4456 definitely_infinite2(integer_set(X)) :-
4457 X='NATURAL' ; X='NATURAL1' ; X='INTEGER'.
4458 definitely_infinite2(seq1(S)) :- definitely_not_empty_set(S).
4459 definitely_infinite2(seq(S)) :- definitely_not_empty_set(S).
4460 definitely_infinite2(cartesian_product(A,B)) :- is_infinite_cart_prod(A,B).
4461 definitely_infinite2(pow_subset(S)) :- definitely_infinite(S).
4462 definitely_infinite2(pow1_subset(S)) :- definitely_infinite(S).
4463 definitely_infinite2(fin_subset(S)) :- definitely_infinite(S). % the set of finite subsets is infinite
4464 definitely_infinite2(fin1_subset(S)) :- definitely_infinite(S).
4465 definitely_infinite2(iseq(S)) :- definitely_infinite(S).
4466 definitely_infinite2(iseq1(S)) :- definitely_infinite(S).
4467 definitely_infinite2(perm(S)) :- definitely_infinite(S).
4468 ?definitely_infinite2(value(V)) :- infinite_value_set(V).
4469 definitely_infinite2(relations(A,B)) :- is_infinite_cart_prod(A,B).
4470 definitely_infinite2(partial_function(A,B)) :- is_infinite_cart_prod(A,B).
4471 definitely_infinite2(total_function(A,B)) :-
4472 (definitely_infinite(A) -> definitely_not_empty_set_card_gt1(B) % INTEGER-->{1} is finite, INTEGER-->BOOL is inf.
4473 ? ; definitely_infinite(B) -> definitely_not_empty_set(A)). % {TRUE} --> INTEGER is infinite
4474 definitely_infinite2(total_relation(A,B)) :- definitely_infinite2(total_function(A,B)).
4475 % TODO: partial_injection, total_injection, partial_surjection, total_surjection, total_bijection, partial_bijection,
4476 % surjection_relation, total_surjection_relation
4477 % cf total_surjection_card
4478
4479 % non empty sets with at least two elements:
4480 definitely_not_empty_set_card_gt1(bool_set).
4481 definitely_not_empty_set_card_gt1(A) :- definitely_infinite(A).
4482
4483 is_infinite_cart_prod(A,B) :-
4484 (definitely_infinite(A) -> definitely_not_empty_set(B) % INTEGER*{} is finite
4485 ? ; definitely_infinite(B) -> definitely_not_empty_set(A)).
4486
4487 infinite_value_set(V) :- var(V),!,fail.
4488 infinite_value_set(global_set(X)) :-
4489 X='NATURAL' ; X='NATURAL1' ; X='INTEGER'.
4490 infinite_value_set(closure(P,T,B)) :-
4491 % T \= [integer], % otherwise we could intersect with NATURAL,... ????
4492 custom_explicit_sets:is_infinite_closure(P,T,B).
4493 infinite_value_set(freetype(ID)) :- kernel_freetypes:is_infinite_freetype(ID).
4494
4495
4496
4497
4498 % check if %(Ids).(Pred|_) is infinite
4499 infinite_or_symbolic_domain_for_lambda(Ids,b(Expr,pred,_),Kind) :-
4500 ? inf_dom_aux(Expr,Ids,Kind).
4501 inf_dom_aux(member(TID2,InfSet),[TID],infinite) :- same_id(TID,TID2,_),
4502 ? definitely_infinite(InfSet).
4503 inf_dom_aux(truth,Ids,infinite) :-
4504 ? member(TID,Ids), get_texpr_type(TID,Type),
4505 is_infinite_ground_type(Type).
4506 inf_dom_aux(COMP,[TID|_],infinite) :- % A \= "" or A \= 0 or A \= B+1 or A < B*B or ...
4507 ? binary_inf_comparison(COMP,A,B),
4508 get_texpr_type(TID,Type), is_infinite_ground_type(Type),
4509 same_id(A,TID,ID),
4510 not_occurs_in_expr(ID,B). % ensure B does not depend on A; e.g. we have %x.(x/=x | E) is empty
4511 inf_dom_aux(equal(EXTFUN,BOOL),Ids,symbolic) :-
4512 % something like f = %s.(s:STRING & REGEX_MATCH(s,"[a-z]+")=TRUE | s)
4513 get_texpr_expr(EXTFUN,external_function_call(FUN,Args)),
4514 get_texpr_boolean(BOOL,_),
4515 is_symbolic_ext_pred(FUN,Args,Ids).
4516 inf_dom_aux(external_pred_call(FUN,Args),Ids,symbolic) :-
4517 is_symbolic_ext_pred(FUN,Args,Ids).
4518
4519 % a comparator which allows infinitely many values for first argument with fixed second argument
4520 binary_inf_comparison(not_equal(A,B),AA,BB) :- sym_unify(A,B,AA,BB).
4521 binary_inf_comparison(less(A,B),AA,BB) :- sym_unify(A,B,AA,BB).
4522 binary_inf_comparison(less_equal(A,B),AA,BB) :- sym_unify(A,B,AA,BB).
4523 ?binary_inf_comparison(greater(A,B),AA,BB) :- sym_unify(A,B,AA,BB).
4524 binary_inf_comparison(greater_equal(A,B),AA,BB) :- sym_unify(A,B,AA,BB).
4525
4526 sym_unify(A,B,A,B).
4527 sym_unify(A,B,B,A).
4528
4529 % not guaranteed to be infinite, but makes sense to keep symbolic
4530 is_symbolic_ext_pred(FUN,Args,[TID|_]) :-
4531 symbolic_ext_pred(FUN),
4532 def_get_texpr_id(TID,ID),
4533 (member(A,Args), occurs_in_expr(ID,A) -> true). % ensure the condition is not static and depends on an argument
4534
4535 % external predicates which indicate that the corresponding function should be kept symbolic
4536 symbolic_ext_pred('IS_REGEX').
4537 symbolic_ext_pred('GET_IS_REGEX').
4538 symbolic_ext_pred('REGEX_MATCH').
4539 symbolic_ext_pred('REGEX_IMATCH').
4540 symbolic_ext_pred('GET_IS_REGEX_MATCH').
4541 symbolic_ext_pred('GET_IS_REGEX_IMATCH').
4542 symbolic_ext_pred('STRING_EQUAL_CASE_INSENSITIVE').
4543 symbolic_ext_pred('STRING_IS_ALPHANUMERIC').
4544 symbolic_ext_pred('STRING_IS_DECIMAL').
4545 symbolic_ext_pred('STRING_IS_NUMBER').
4546 symbolic_ext_pred('GET_STRING_EQUAL_CASE_INSENSITIVE').
4547 symbolic_ext_pred('GET_STRING_IS_ALPHANUMERIC').
4548 symbolic_ext_pred('GET_STRING_IS_DECIMAL').
4549 symbolic_ext_pred('GET_STRING_IS_NUMBER').
4550 symbolic_ext_pred('GET_STRING_IS_INT').
4551 symbolic_ext_pred('FILE_EXISTS').
4552 symbolic_ext_pred('GET_FILE_EXISTS').
4553 symbolic_ext_pred('DIRECTORY_EXISTS').
4554 symbolic_ext_pred('GET_DIRECTORY_EXISTS').
4555 % TODO: add more external predicates
4556 %symbolic_ext_pred(X) :- nl,print(not_symbolic(X)),nl,fail.
4557
4558
4559 :- use_module(custom_explicit_sets,[quick_is_definitely_maximal_set/1]).
4560 % should we use is_just_type/1 instead ?? TO DO: check
4561 definitely_maximal_set(b(S,_,_)) :- definitely_maximal2(S).
4562 definitely_maximal2(integer_set('INTEGER')).
4563 definitely_maximal2(bool_set).
4564 definitely_maximal2(string_set).
4565 definitely_maximal2(typeset).
4566 definitely_maximal2(comprehension_set(_,b(truth,_,_))). % also covers is_integer_set(X,'INTEGER')
4567 definitely_maximal2(value(S)) :- nonvar(S),quick_is_definitely_maximal_set(S).
4568 % TO DO: cartesian product, records, ... if useful
4569
4570
4571 % just a sequence consisting of a single element
4572 is_singleton_sequence(b(sequence_extension([ELEMENT]),_,_),ELEMENT).
4573
4574 % check if type does not contain sets
4575 type_contains_no_sets(X) :- var(X),!,fail. % in test 472 we have a variable type
4576 type_contains_no_sets(integer).
4577 type_contains_no_sets(boolean).
4578 type_contains_no_sets(string).
4579 type_contains_no_sets(global(_)).
4580 type_contains_no_sets(couple(A,B)) :- type_contains_no_sets(A), type_contains_no_sets(B).
4581 type_contains_no_sets(record(Fields)) :- field_types_ok(Fields).
4582 field_types_ok([]).
4583 field_types_ok([field(_,Type)|T]) :- type_contains_no_sets(Type), field_types_ok(T).
4584
4585 % ----------------------------------
4586
4587
4588 select_conjunct(Predicate,Conjunction,Prefix,Suffix) :-
4589 conjunction_to_list(Conjunction,List),
4590 append(Prefix,[Predicate|Suffix],List).
4591
4592
4593 data_validation_mode :-
4594 (get_preference(data_validation_mode,true) -> true
4595 ; environ(prob_data_validation_mode,true)).
4596
4597 % optionally provide hints about rewritings or potential improvements
4598 add_hint_message(_,_Msg,_Term,_Span) :- debug_mode(off),
4599 get_preference(performance_monitoring_on,false), % should we use another preference?
4600 !.
4601 add_hint_message(Src,Msg,Term,Span) :-
4602 add_message(Src,Msg,Term,Span).
4603
4604 % ------------------------
4605
4606 % mini partial evaluation / constant expression evaluation of B expressions
4607 % TO DO: unify with b_compile and b_expression_sharing !
4608 % But this one only pre-computes top-level operators; assumes bottom-up traversal
4609
4610 :- use_module(kernel_card_arithmetic,[safe_pown/3]).
4611 :- use_module(library(avl),[avl_size/2]).
4612 pre_compute_static_int_expression(add(A,B),Result) :- % plus
4613 get_integer(A,IA), get_integer(B,IB),
4614 Result is IA+IB.
4615 pre_compute_static_int_expression(minus(A,B),Result) :- % plus
4616 get_integer(A,IA), get_integer(B,IB),
4617 Result is IA-IB.
4618 pre_compute_static_int_expression(unary_minus(A),Result) :- % plus
4619 get_integer(A,IA),
4620 Result is -IA.
4621 pre_compute_static_int_expression(multiplication(A,B),Result) :-
4622 get_integer(A,IA), get_integer(B,IB),
4623 Result is IA*IB.
4624 pre_compute_static_int_expression(div(A,B),Result) :- % TO DO: also add floored_div
4625 get_integer(B,IB), IB \= 0,
4626 get_integer(A,IA),
4627 Result is IA//IB.
4628 pre_compute_static_int_expression(modulo(A,B),Result) :-
4629 get_integer(B,IB), IB > 0,
4630 get_integer(A,IA), IA >= 0,
4631 Result is IA mod IB.
4632 pre_compute_static_int_expression(power_of(A,B),Result) :-
4633 get_integer(A,IA), get_integer(B,IB), IB >= 0,
4634 safe_pown(IA,IB,Result), number(Result).
4635 pre_compute_static_int_expression(card(A),Result) :-
4636 get_nonvar_val(A,AVal),
4637 (AVal=[] -> Result=0
4638 ; AVal=avl_set(AVL) -> avl_size(AVL,Result)).
4639
4640 get_nonvar_val(b(value(V),_,_),V) :- nonvar(V).
4641
4642 % ----------------------------------------
4643