| 1 | % (c) 2021-2024 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
| 2 | % Heinrich Heine Universitaet Duesseldorf | |
| 3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
| 4 | :- module(ast_optimizer_for_smt, [reset_optimizer_state/0, | |
| 5 | precompute_values/3, | |
| 6 | precompute_values_non_recursive/3, | |
| 7 | assert_state_id_values/2, | |
| 8 | assert_ground_id_values/2, | |
| 9 | replace_ids_with_ground_values/4, | |
| 10 | replace_ids_with_eq_ids_top_level/2]). | |
| 11 | ||
| 12 | :- use_module(library(lists)). | |
| 13 | ||
| 14 | :- use_module(smt_solvers_interface(set_rewriter)). | |
| 15 | :- use_module(smt_solvers_interface(quantifier_instantiation)). | |
| 16 | :- use_module(probsrc(preferences), [get_preference/2]). | |
| 17 | :- use_module(probsrc(kernel_objects), [infer_value_type/2]). | |
| 18 | :- use_module(probsrc(bmachine), [b_get_named_machine_set/2]). | |
| 19 | :- use_module(probsrc(b_interpreter), [b_compute_expression_nowf/6]). | |
| 20 | :- use_module(probsrc(bsyntaxtree), [remove_all_infos/2, | |
| 21 | get_texpr_id/2, | |
| 22 | get_texpr_expr/2, | |
| 23 | get_texpr_type/2, | |
| 24 | safe_create_texpr/4, | |
| 25 | find_identifier_uses/3, | |
| 26 | conjunction_to_list/2, | |
| 27 | syntaxtransformation/5]). | |
| 28 | :- use_module(probsrc(store), [normalise_value_for_var/4]). | |
| 29 | :- use_module(probsrc(module_information), [module_info/2]). | |
| 30 | :- use_module(probsrc(error_manager), [add_internal_error/2]). | |
| 31 | :- use_module(probsrc(kernel_tools), [ground_value/1]). | |
| 32 | %:- use_module(wdsrc(well_def_analyser), [prove_sequent/1]). | |
| 33 | ||
| 34 | :- module_info(group, smt_solvers). | |
| 35 | :- module_info(description, 'This module implements transformations to simplify SMT-LIB translations.'). | |
| 36 | ||
| 37 | :- dynamic id_ground_value/3, id_equality/3. | |
| 38 | ||
| 39 | reset_optimizer_state :- | |
| 40 | retractall(id_ground_value(_,_,_)), | |
| 41 | retractall(id_equality(_,_,_)). | |
| 42 | ||
| 43 | %% precompute_values(+Ast, -AstOpt). | |
| 44 | % Compute some ground operations in Prolog to simplify SMT-Lib constraints. | |
| 45 | % We split this from the actual cleanup since we use information on ground | |
| 46 | % values in the cleanup, e.g., to optimize cardinality constraints. | |
| 47 | % Computes some more values than b_compile/6. | |
| 48 | precompute_values(Ast, Options, AstOpt) :- | |
| 49 | precompute_values3(Ast, [allow_recursive_call|Options], [], AstOpt). | |
| 50 | ||
| 51 | precompute_values_non_recursive(Options, Ast, AstOpt) :- | |
| 52 | precompute_values_non_recursive(Options, [], Ast, AstOpt). | |
| 53 | ||
| 54 | precompute_values_non_recursive(Options, ScopeIds, Ast, AstOpt) :- | |
| 55 | precompute_values3(Ast, Options, ScopeIds, AstOpt). | |
| 56 | ||
| 57 | precompute_values3(Ast, Options, ScopeIds, AstOpt) :- | |
| 58 | Ast = b(Expr,Type,Info), !, | |
| 59 | ( precompute_values_e(Expr, Type, Info, Options, ScopeIds, NExpr) | |
| 60 | -> safe_create_texpr(NExpr, Type, Info, AstOpt) | |
| 61 | ; add_internal_error('Call failed: ',precompute_values_e(Expr, Type, Info, Options, ScopeIds, _)), | |
| 62 | AstOpt = Ast | |
| 63 | ). | |
| 64 | precompute_values3(L, _, _, R) :- | |
| 65 | add_internal_error('Not a B AST:', precompute_values3(L, _, _, R) ), | |
| 66 | R = L. | |
| 67 | ||
| 68 | precompute_values_e(min_int, _, _, _, _, integer(MinInt)) :- !, | |
| 69 | get_preference(minint, MinInt). | |
| 70 | precompute_values_e(max_int, _, _, _, _, integer(MaxInt)) :- !, | |
| 71 | get_preference(maxint, MaxInt). | |
| 72 | /*precompute_values_e(function(Fun,Elm), Type, Info, _, AstOpt) :- | |
| 73 | Ast = b(function(Fun,Elm),Type,Info), | |
| 74 | Fun \= b(identifier(_),_,_), % uninterpreted functions can not be precomputed | |
| 75 | prove_sequent(Ast), | |
| 76 | !, | |
| 77 | find_typed_identifier_uses(Ast, [], UsedIds), | |
| 78 | set_up_typed_localstate(UsedIds, _, SmtTypedVals, [], Bindings, positive), | |
| 79 | init_wait_flags(WFStore, [wf_fun]), | |
| 80 | ( b_tighter_enumerate_all_values(SmtTypedVals, WFStore), | |
| 81 | b_interpreter:b_compute_expression(Ast, Bindings, Bindings, Res, WFStore), | |
| 82 | ground_wait_flags(WFStore) | |
| 83 | -> AstOpt = b(value(Res),Type,Info) | |
| 84 | ; AstOpt = Ast | |
| 85 | ).*/ | |
| 86 | precompute_values_e(forall(TIDs,LHS,RHS), pred, Info, Options, ScopeIds, Res) :- | |
| 87 | member(instantiate_quantifier_limit(L), Options), | |
| 88 | L \== 0, | |
| 89 | instantiate_quantifier_possible(forall(TIDs,LHS,RHS), pred, Info, Options, ScopeIds, NOptions, TRes), | |
| 90 | ( TRes == truth | |
| 91 | -> Res = truth % no instantiation found, universal quantification is true | |
| 92 | ; precompute_values3(TRes, NOptions, [], b(Res,pred,_)) % TODO: check this, what about ScopeIDs? | |
| 93 | ). | |
| 94 | precompute_values_e(exists(TIDs,LHS), pred, Info, Options, ScopeIds, Res) :- | |
| 95 | member(instantiate_quantifier_limit(L), Options), | |
| 96 | L \== 0, | |
| 97 | instantiate_quantifier_possible(exists(TIDs,LHS), pred, Info, Options, ScopeIds, NOptions, TRes), | |
| 98 | ( TRes == falsity | |
| 99 | -> Res = falsity % no instantiation found, existential quantification is false | |
| 100 | ; precompute_values3(TRes, NOptions, [], b(Res,pred,_)) % TODO: check this, what about ScopeIDs? | |
| 101 | ). | |
| 102 | precompute_values_e(comprehension_set([Id,LambdaRes],Body), _, _, Options, ScopeIds, SetExtension) :- | |
| 103 | % e.g., %(i : 1..3|0) | |
| 104 | LambdaRes = b(identifier('_lambda_result_'),_,_), | |
| 105 | Body = b(conjunct(Member,LambdaEq),pred,_), | |
| 106 | remove_all_infos(Id, CId), | |
| 107 | remove_all_infos(LambdaRes, CLambdaRes), | |
| 108 | Member = b(member(CId,Domain),pred,_), | |
| 109 | LambdaEq = b(equal(CLambdaRes,RangeVal),pred,_), | |
| 110 | compute_ground_lambda(Id, Domain, RangeVal, ScopeIds, Options, ComputedSet), | |
| 111 | !, | |
| 112 | construct_set_extension(ComputedSet,SetExtension). | |
| 113 | precompute_values_e(interval(A,B), _, _, Options, ScopeIds, SetExtension) :- | |
| 114 | rewrite_set_to_list_of_asts(b(interval(A,B),set(integer),[]), ScopeIds, Options, List), | |
| 115 | !, | |
| 116 | construct_set_extension(List,SetExtension). | |
| 117 | precompute_values_e(Pow, _, _, Options, ScopeIds, Precomputed) :- | |
| 118 | SetAst = b(set_extension(List),set(Type),[]), | |
| 119 | ( Pow = pow_subset(Arg), | |
| 120 | PowAst = b(pow_subset(SetAst),set(set(Type)),[]) | |
| 121 | ; Pow = pow1_subset(Arg), | |
| 122 | PowAst = b(pow1_subset(SetAst),set(set(Type)),[]) | |
| 123 | ), | |
| 124 | rewrite_set_to_list_of_asts(Arg, ScopeIds, Options, List), | |
| 125 | is_a_list_of_values(List), | |
| 126 | length(List, Len), | |
| 127 | ( Pow = pow_subset(Arg) | |
| 128 | -> PowCard is 2**Len | |
| 129 | ; PowCard is 2**Len - 1 | |
| 130 | ), | |
| 131 | ( member(instantiate_sets_limit(SetLimit), Options) | |
| 132 | -> PowCard =< SetLimit | |
| 133 | ; true | |
| 134 | ), | |
| 135 | List = [b(_,Type,_)|_], | |
| 136 | !, | |
| 137 | b_compute_expression_nowf(PowAst, [], [], TPrecomputedVal,'SMT precomputation',0), | |
| 138 | normalise_value_for_var(solver_result, force, TPrecomputedVal, PrecomputedVal), | |
| 139 | Precomputed = value(PrecomputedVal). | |
| 140 | precompute_values_e(reverse(Arg), _, _, Options, ScopeIds, SetExtension) :- | |
| 141 | rewrite_set_to_list_of_asts(Arg, ScopeIds, Options, List), | |
| 142 | is_a_list_of_values(List), | |
| 143 | compute_inverse(List, InvList), | |
| 144 | !, | |
| 145 | construct_set_extension(InvList,SetExtension). | |
| 146 | precompute_values_e(cartesian_product(SetA,SetB), _, _, Options, ScopeIds, SetExtension) :- | |
| 147 | rewrite_set_to_list_of_asts(SetA, ScopeIds, Options, ListA), | |
| 148 | rewrite_set_to_list_of_asts(SetB, ScopeIds, Options, ListB), | |
| 149 | get_texpr_type(SetA, set(InTypeA)), | |
| 150 | get_texpr_type(SetB, set(InTypeB)), | |
| 151 | compute_cartesian_product(InTypeA, InTypeB, ListA, ListB, CartList), | |
| 152 | !, | |
| 153 | construct_set_extension(CartList,SetExtension). | |
| 154 | precompute_values_e(BOP, _Type, _Info, Options, ScopeIds, Res) :- | |
| 155 | binary_connective(BOP,F,A,B), | |
| 156 | !, | |
| 157 | binary_connective(Res,F,NA,NB), | |
| 158 | precompute_values_non_recursive(Options, ScopeIds, A, NA), | |
| 159 | precompute_values_non_recursive(Options, ScopeIds, B, NB). % we ignore allow_recursive_call option; all optimisations are local | |
| 160 | precompute_values_e(Expr, Type, Info, Options, ScopeIds, Res) :- | |
| 161 | syntaxtransformation(Expr, Subs, Names, NSubs, NExpr), | |
| 162 | ( Names \= [] | |
| 163 | -> maplist(get_texpr_id, Names, IdNames), | |
| 164 | append(IdNames, ScopeIds, NScopeIds) | |
| 165 | ; NScopeIds = ScopeIds), | |
| 166 | !, | |
| 167 | maplist(precompute_values_non_recursive(Options, NScopeIds), Subs, NSubs), | |
| 168 | ( ( select(allow_recursive_call, Options, Options2), | |
| 169 | \+ no_recursive_computation(NExpr) | |
| 170 | ) | |
| 171 | -> precompute_values_e(NExpr, Type, Info, Options2, NScopeIds, Res) % Warning: this can cause a quadratic processing time ! | |
| 172 | ; Res = NExpr | |
| 173 | ). | |
| 174 | precompute_values_e(Expr, _, _, _, _, Res) :- | |
| 175 | add_internal_error('Unknown AST node:', precompute_values_e(Expr, _, _, _, Res)), | |
| 176 | Res = Expr. | |
| 177 | ||
| 178 | no_recursive_computation(set_extension(_)). | |
| 179 | no_recursive_computation(sequence_extension(_)). | |
| 180 | no_recursive_computation(value(_)). | |
| 181 | ||
| 182 | binary_connective(conjunct(A,B),conjunct,A,B). | |
| 183 | binary_connective(disjunct(A,B),disjunct,A,B). | |
| 184 | binary_connective(implication(A,B),implication,A,B). | |
| 185 | binary_connective(equivalence(A,B),equivalence,A,B). | |
| 186 | ||
| 187 | construct_set_extension([],Res) :- !, Res=empty_set. | |
| 188 | construct_set_extension(List,set_extension(List)). | |
| 189 | ||
| 190 | is_asserted_ground_value(IdName, _, GroundAst) :- | |
| 191 | id_ground_value(_, IdName, TGroundAst), | |
| 192 | !, | |
| 193 | GroundAst = TGroundAst. | |
| 194 | is_asserted_ground_value(IdName, EqVals, GroundAst) :- | |
| 195 | % transitive equality | |
| 196 | (id_equality(_, IdName, EqIdName); id_equality(_, EqIdName, IdName)), | |
| 197 | \+ member(EqIdName, EqVals), | |
| 198 | !, | |
| 199 | is_asserted_ground_value(EqIdName, [EqIdName|EqVals], GroundAst). | |
| 200 | ||
| 201 | % TO DO: improve, use a normalized order for equalities | |
| 202 | replace_ids_with_eq_ids_top_level(Pred, NPred) :- | |
| 203 | Pred = b(Expr,Type,Info), | |
| 204 | replace_ids_with_eq_ids_top_level_e(Expr, NExpr), | |
| 205 | NPred = b(NExpr,Type,Info). | |
| 206 | ||
| 207 | % all solutions on bt | |
| 208 | replace_ids_with_eq_ids_top_level_e(identifier(IdName), NExpr) :- | |
| 209 | findall(A-B-C, id_equality(A,B,C), _), | |
| 210 | (id_equality(0, IdName, EqIdName); id_equality(0, EqIdName, IdName)), | |
| 211 | NExpr = identifier(EqIdName). | |
| 212 | replace_ids_with_eq_ids_top_level_e(negation(TExpr), NExpr) :- | |
| 213 | !, | |
| 214 | replace_ids_with_eq_ids_top_level(TExpr, NTExpr), | |
| 215 | NExpr = negation(NTExpr). | |
| 216 | replace_ids_with_eq_ids_top_level_e(Expr, NExpr) :- | |
| 217 | Expr \= identifier(_), | |
| 218 | syntaxtransformation(Expr,Subs,_,NSubs,NExpr), | |
| 219 | !, | |
| 220 | l_replace_ids_with_eq_ids_top_level(Subs,NSubs). | |
| 221 | replace_ids_with_eq_ids_top_level_e(Expr, Expr) :- | |
| 222 | Expr \= identifier(_). | |
| 223 | ||
| 224 | l_replace_ids_with_eq_ids_top_level([],[]). | |
| 225 | l_replace_ids_with_eq_ids_top_level([H|T],[NH|NT]) :- | |
| 226 | replace_ids_with_eq_ids_top_level(H,NH), | |
| 227 | l_replace_ids_with_eq_ids_top_level(T,NT). | |
| 228 | ||
| 229 | %% replace_ids_with_ground_values(+Ast, +ScopeId, +ExcludeScopeIds, -AstOpt). | |
| 230 | % Replace S in card(S) if S is a ground finite set, e.g., a ground interval. | |
| 231 | % Do not optimise scoped ids if they have the same name as a top-level variable, | |
| 232 | % e.g., as introduced by an existential quantifier. | |
| 233 | % +ScopeId is used when entering scopes or dependend logical operators such as implication or disjunction to assert scoped id equalities. | |
| 234 | % This predicate uses the dynamic facts id_ground_value/3 and is_asserted_ground_value/3. | |
| 235 | replace_ids_with_ground_values(Ast, ScopeId, ExcludeScopeIds, AstOpt) :- | |
| 236 | Ast = b(Expr,Type,Info), | |
| 237 | \+ member(do_not_optimize_away, Info), | |
| 238 | !, | |
| 239 | replace_ids_with_ground_values_e(Expr, ScopeId, ExcludeScopeIds, NExpr), | |
| 240 | AstOpt = b(NExpr,Type,Info). | |
| 241 | replace_ids_with_ground_values(A, _, _, A). | |
| 242 | ||
| 243 | replace_ids_with_ground_values_e(identifier(IdName), _, ExcludeScopeIds, NExpr) :- | |
| 244 | is_asserted_ground_value(IdName, [], GroundAst), | |
| 245 | !, | |
| 246 | ( \+ member(b(identifier(IdName),_,_), ExcludeScopeIds) | |
| 247 | -> get_texpr_expr(GroundAst, NExpr) | |
| 248 | ; NExpr = identifier(IdName) | |
| 249 | ). | |
| 250 | replace_ids_with_ground_values_e(Eq, _, _, NEq) :- | |
| 251 | % do not remove ground equality since this will be the last occurrence of the variable | |
| 252 | ( Eq = equal(Id,GroundValOrId) | |
| 253 | ; Eq = equal(GroundValOrId,Id) | |
| 254 | ), | |
| 255 | ( Id = b(identifier(_),_,_), | |
| 256 | get_texpr_expr(GroundValOrId, GroundExpr), | |
| 257 | is_ground_b_value(GroundExpr) | |
| 258 | ; id_equality(_, Id, GroundValOrId) | |
| 259 | ; id_equality(_, GroundValOrId, Id) | |
| 260 | ), | |
| 261 | !, | |
| 262 | NEq = Eq. | |
| 263 | replace_ids_with_ground_values_e(equal(Id1,Id2), _, ExcludeScopeIds, NEq) :- | |
| 264 | Id1 = b(identifier(IdName1),Type,_), | |
| 265 | Id2 = b(identifier(IdName2),Type,_), | |
| 266 | ( id_ground_value(_, IdName1, GroundAst1), | |
| 267 | \+ member(b(identifier(IdName1),Type,_), ExcludeScopeIds) | |
| 268 | -> NArg1 = GroundAst1 | |
| 269 | ; NArg1 = Id1 | |
| 270 | ), | |
| 271 | ( id_ground_value(_, IdName2, GroundAst2), | |
| 272 | \+ member(b(identifier(IdName2),Type,_), ExcludeScopeIds) | |
| 273 | -> NArg2 = GroundAst2 | |
| 274 | ; NArg2 = Id2 | |
| 275 | ), | |
| 276 | !, | |
| 277 | NEq = equal(NArg1,NArg2). | |
| 278 | replace_ids_with_ground_values_e(set_extension([Id]), _, ExcludeScopeIds, NUnary) :- | |
| 279 | Id = b(identifier(IdName),_,_), | |
| 280 | \+ member(b(identifier(IdName),_,_), ExcludeScopeIds), | |
| 281 | !, | |
| 282 | ( id_ground_value(_, IdName, GroundVal) | |
| 283 | -> NUnary = set_extension([GroundVal]) | |
| 284 | ; NUnary = set_extension([Id]) | |
| 285 | ). | |
| 286 | replace_ids_with_ground_values_e(Expr, ScopeId, ExcludeScopeIds, NExpr) :- | |
| 287 | functor(Expr, Functor, 2), | |
| 288 | ( Functor = implication | |
| 289 | ; Functor = disjunct | |
| 290 | ), | |
| 291 | arg(1, Expr, Arg1), | |
| 292 | arg(2, Expr, Arg2), | |
| 293 | !, | |
| 294 | NScopeId is ScopeId + 1, | |
| 295 | assert_ground_id_values(NScopeId, Arg1), | |
| 296 | replace_ids_with_ground_values(Arg1, NScopeId, ExcludeScopeIds, NArg1), | |
| 297 | retract_ground_id_values_for_scope(NScopeId), | |
| 298 | assert_ground_id_values(NScopeId, Arg2), | |
| 299 | replace_ids_with_ground_values(Arg2, NScopeId, ExcludeScopeIds, NArg2), | |
| 300 | retract_ground_id_values_for_scope(NScopeId), | |
| 301 | functor(NExpr, Functor, 2), | |
| 302 | arg(1, NExpr, NArg1), | |
| 303 | arg(2, NExpr, NArg2). | |
| 304 | replace_ids_with_ground_values_e(Expr, ScopeId, ExcludeScopeIds, NExpr) :- | |
| 305 | functor(Expr, Functor, 2), | |
| 306 | ( Functor = conjunct | |
| 307 | ; Functor = equivalence | |
| 308 | ), | |
| 309 | arg(1, Expr, Arg1), | |
| 310 | arg(2, Expr, Arg2), | |
| 311 | !, | |
| 312 | % ground ids already asserted for top-level conjunction | |
| 313 | replace_ids_with_ground_values(Arg1, ScopeId, ExcludeScopeIds, NArg1), | |
| 314 | replace_ids_with_ground_values(Arg2, ScopeId, ExcludeScopeIds, NArg2), | |
| 315 | functor(NExpr, Functor, 2), | |
| 316 | arg(1, NExpr, NArg1), | |
| 317 | arg(2, NExpr, NArg2). | |
| 318 | replace_ids_with_ground_values_e(negation(Pred), ScopeId, ExcludeScopeIds, NNeg) :- | |
| 319 | !, | |
| 320 | NScopeId is ScopeId + 1, | |
| 321 | assert_ground_id_values(NScopeId, Pred), | |
| 322 | replace_ids_with_ground_values(Pred, NScopeId, ExcludeScopeIds, NPred), | |
| 323 | retract_ground_id_values_for_scope(NScopeId), | |
| 324 | NNeg = negation(NPred). | |
| 325 | replace_ids_with_ground_values_e(forall(Ids,Lhs,Rhs), ScopeId, ExcludeScopeIds, NForall) :- | |
| 326 | !, | |
| 327 | append(ExcludeScopeIds, Ids, NExcludeScopeIds), | |
| 328 | replace_ids_with_ground_values(Lhs, ScopeId, NExcludeScopeIds, NLhs), | |
| 329 | NScopeId is ScopeId + 1, | |
| 330 | assert_ground_id_values(NScopeId, Rhs), | |
| 331 | replace_ids_with_ground_values(Rhs, NScopeId, NExcludeScopeIds, NRhs), | |
| 332 | retract_ground_id_values_for_scope(NScopeId), | |
| 333 | NForall = forall(Ids,NLhs,NRhs). | |
| 334 | replace_ids_with_ground_values_e(Expr, ScopeId, ExcludeScopeIds, NExpr) :- | |
| 335 | functor(Expr, Functor, 2), | |
| 336 | ( Functor = exists | |
| 337 | ; Functor = comprehension_set | |
| 338 | ; Functor = lambda | |
| 339 | ; Functor = general_sum | |
| 340 | ; Functor = general_product | |
| 341 | ; Functor = quantified_union | |
| 342 | ; Functor = quantified_intersection | |
| 343 | ; Functor = event_b_comprehension_set | |
| 344 | ), | |
| 345 | !, | |
| 346 | arg(1, Expr, Ids), | |
| 347 | arg(2, Expr, Pred), | |
| 348 | NScopeId is ScopeId + 1, | |
| 349 | assert_ground_id_values(NScopeId, Pred), | |
| 350 | append(ExcludeScopeIds, Ids, NExcludeScopeIds), | |
| 351 | replace_ids_with_ground_values(Pred, NScopeId, NExcludeScopeIds, NPred), | |
| 352 | retract_ground_id_values_for_scope(NScopeId), | |
| 353 | functor(NExpr, Functor, 2), | |
| 354 | arg(1, NExpr, Ids), | |
| 355 | arg(2, NExpr, NPred). | |
| 356 | replace_ids_with_ground_values_e(Expr, ScopeId, ExcludeScopeIds,NExpr) :- | |
| 357 | syntaxtransformation(Expr,Subs,Names,NSubs,NExpr), | |
| 358 | !, | |
| 359 | append(Names, ExcludeScopeIds, NExcludeScopeIds), | |
| 360 | l_replace_ids_with_ground_values(Subs,ScopeId,NExcludeScopeIds,NSubs). | |
| 361 | replace_ids_with_ground_values_e(Expr, _, _, Expr). | |
| 362 | ||
| 363 | ||
| 364 | l_replace_ids_with_ground_values([],_,_,[]). | |
| 365 | l_replace_ids_with_ground_values([H|T],ScopeId,Excl,[NH|NT]) :- | |
| 366 | replace_ids_with_ground_values(H,ScopeId,Excl,NH), | |
| 367 | l_replace_ids_with_ground_values(T,ScopeId,Excl,NT). | |
| 368 | ||
| 369 | ||
| 370 | %% assert_state_id_values(+ProBState). | |
| 371 | % Assert global state id values to be replaced syntactically afterwards. | |
| 372 | assert_state_id_values(_, NoState) :- | |
| 373 | NoState == no_state, !. | |
| 374 | assert_state_id_values(_, []). | |
| 375 | assert_state_id_values(TypedIds, [bind(IdName,Value)|T]) :- | |
| 376 | ( member(b(identifier(IdName),TType,_), TypedIds) | |
| 377 | -> Type = TType | |
| 378 | ; infer_value_type(Value, Type) | |
| 379 | ), | |
| 380 | asserta(id_ground_value(0,IdName,b(value(Value),Type,[]))), | |
| 381 | assert_state_id_values(TypedIds, T). | |
| 382 | ||
| 383 | %% assert_ground_id_values(+ScopeId, +Ast). | |
| 384 | % Assert ground id values originating from identifier equalities such as x=[1,2]. | |
| 385 | % We are then able to precompute some constraints which improves performance. | |
| 386 | % See, for instance, :z3 x = [2,3] & y = x~ & d = y[{3,4}], where Z3 answers unknown without rewriting. | |
| 387 | % Note: Only obvious ground equalities on the top-level of a conjunction are considered. | |
| 388 | % +ScopeId is an integer, 0 at the top-level and +1 for each quantified scope. | |
| 389 | assert_ground_id_values(ScopeId, Ast) :- | |
| 390 | conjunction_to_list(Ast, List), | |
| 391 | maplist(assert_ground_id_value(ScopeId), List). | |
| 392 | ||
| 393 | assert_ground_id_value(ScopeId, b(Eq,pred,_)) :- | |
| 394 | ( Eq = equal(Id,GroundAst) | |
| 395 | ; Eq = equal(GroundAst,Id) | |
| 396 | ), | |
| 397 | Id = b(identifier(IdName),_,_), | |
| 398 | get_texpr_expr(GroundAst, GroundVal), | |
| 399 | is_ground_b_value(GroundVal), | |
| 400 | !, | |
| 401 | asserta(id_ground_value(ScopeId,IdName,GroundAst)). | |
| 402 | assert_ground_id_value(ScopeId, b(equal(Id1,Id2),pred,_)) :- | |
| 403 | Id1 = b(identifier(IdName1),_,_), | |
| 404 | Id2 = b(identifier(IdName2),_,_), | |
| 405 | !, | |
| 406 | asserta(id_equality(ScopeId,IdName1,IdName2)). | |
| 407 | assert_ground_id_value(_, _). | |
| 408 | ||
| 409 | retract_ground_id_values_for_scope(ScopeId) :- | |
| 410 | retractall(id_ground_value(ScopeId,_,_)), | |
| 411 | retractall(id_equality(ScopeId,_,_)). | |
| 412 | ||
| 413 | is_ground_b_value(boolean_false). | |
| 414 | is_ground_b_value(boolean_true). | |
| 415 | is_ground_b_value(bool_set). | |
| 416 | is_ground_b_value(empty_sequence). | |
| 417 | is_ground_b_value(empty_set). | |
| 418 | is_ground_b_value(integer(_)). | |
| 419 | is_ground_b_value(int(_)). | |
| 420 | is_ground_b_value(integer_set(_)). | |
| 421 | is_ground_b_value(max_int). | |
| 422 | is_ground_b_value(min_int). | |
| 423 | is_ground_b_value(float_set). | |
| 424 | is_ground_b_value(real(_)). | |
| 425 | is_ground_b_value(real_set). | |
| 426 | is_ground_b_value(string(_)). | |
| 427 | is_ground_b_value(string_set). | |
| 428 | is_ground_b_value(value(_)). | |
| 429 | is_ground_b_value(interval(_,_)). | |
| 430 | is_ground_b_value(set_extension(_)). | |
| 431 | is_ground_b_value(identifier(EnumId)) :- | |
| 432 | b_get_named_machine_set(_, EnumIds), | |
| 433 | member(EnumId, EnumIds), !. | |
| 434 | ||
| 435 | %% compute_ground_lambda(+LambdaId, +Domain, +Range, +ScopeIds, +Options, -ComputedSet) | |
| 436 | compute_ground_lambda(LambdaId, Domain, Range, ScopeIds, Options, ComputedSet) :- | |
| 437 | rewrite_set_to_list_of_asts(Domain, ScopeIds, Options, LoAsts), | |
| 438 | ( member(instantiate_sets_limit(SetLimit), Options) | |
| 439 | -> length(LoAsts, Len), | |
| 440 | Len =< SetLimit | |
| 441 | ; true | |
| 442 | ), | |
| 443 | get_texpr_type(Range, RT), | |
| 444 | find_identifier_uses(Range, [], UsedIds), | |
| 445 | LambdaId = b(identifier(LambdaIdName),_,_), | |
| 446 | LoAsts = [b(_,DT,_)|_], | |
| 447 | ( UsedIds = [] | |
| 448 | -> % for instance, %i.(i : 1 .. 10|0) | |
| 449 | findall(b(couple(A,Range),couple(DT,RT),[]), | |
| 450 | member(A, LoAsts), | |
| 451 | ComputedSet) | |
| 452 | ; % for instance, %i.(i : 1 .. 10|i + i) | |
| 453 | UsedIds = [LambdaIdName], | |
| 454 | findall(b(couple(A,ComputedRange),couple(DT,RT),[]), | |
| 455 | ( member(A, LoAsts), | |
| 456 | b_compute_expression_nowf(A, [], [], DomValue,'SMT precomputation',0), | |
| 457 | b_compute_expression_nowf(Range, [bind(LambdaIdName,DomValue)], [], Res,'SMT precomputation',0), | |
| 458 | ComputedRange = b(value(Res),RT,[]) | |
| 459 | ), | |
| 460 | ComputedSet) | |
| 461 | ). | |
| 462 | ||
| 463 | %% compute_inverse(+List, -InvList) | |
| 464 | compute_inverse([], []). | |
| 465 | compute_inverse(List, InvList) :- | |
| 466 | List = [Couple|_], | |
| 467 | get_texpr_type(Couple, CoupleType), | |
| 468 | CoupleType = couple(A,B), | |
| 469 | compute_inverse(couple(B,A), List, [], InvList). | |
| 470 | ||
| 471 | compute_inverse(_, [], Acc, Acc). | |
| 472 | compute_inverse(InvCoupleType, [Couple|T], Acc, InvList) :- | |
| 473 | get_elements_of_couple(Couple, A, B), | |
| 474 | compute_inverse(InvCoupleType, T, [b(couple(B,A),InvCoupleType,[])|Acc], InvList). | |
| 475 | ||
| 476 | get_elements_of_couple(b(couple(A,B),_,_), A, B). | |
| 477 | get_elements_of_couple(b(value((A,B)),couple(TA,TB),[]), AstA, AstB) :- | |
| 478 | AstA = b(value(A),TA,[]), | |
| 479 | AstB = b(value(B),TB,[]). | |
| 480 | ||
| 481 | %% compute_cartesian_product(+InTypeA, +InTypeB, +ListA, +ListB, -CartList). | |
| 482 | compute_cartesian_product(InTypeA, InTypeB, ListA, ListB, CartList) :- | |
| 483 | compute_cartesian_product(InTypeA, InTypeB, ListA, ListB, [], CartList). | |
| 484 | ||
| 485 | compute_cartesian_product(_, _, [], _, Acc, Acc). | |
| 486 | compute_cartesian_product(InTypeA, InTypeB, [H|T], SetB, Acc, CartList) :- | |
| 487 | compute_single_cartesian_product(InTypeA, InTypeB, H, SetB, Acc, NAcc), | |
| 488 | compute_cartesian_product(InTypeA, InTypeB, T, SetB, NAcc, CartList). | |
| 489 | ||
| 490 | compute_single_cartesian_product(_, _, _, [], Acc, Acc). | |
| 491 | compute_single_cartesian_product(InTypeA, InTypeB, H, [H2|T], Acc, NAcc) :- | |
| 492 | Couple = b(couple(H,H2),couple(InTypeA, InTypeB),[]), | |
| 493 | NAcc1 = [Couple|Acc], | |
| 494 | compute_single_cartesian_product(InTypeA, InTypeB, H, T, NAcc1, NAcc). | |
| 495 | ||
| 496 | % check if we have a list of values, without identifiers | |
| 497 | is_a_list_of_values([]). | |
| 498 | is_a_list_of_values([H|T]) :- is_a_value(H),is_a_list_of_values(T). | |
| 499 | ||
| 500 | is_a_value(b(V,_,_)) :- is_a_value_aux(V). | |
| 501 | is_a_value_aux(value(V)) :- ground_value(V). | |
| 502 | is_a_value_aux(integer(_)). | |
| 503 | is_a_value_aux(boolean_false). | |
| 504 | is_a_value_aux(boolean_true). | |
| 505 | is_a_value_aux(string(_)). | |
| 506 | is_a_value_aux(real(_)). | |
| 507 | is_a_value_aux(empty_set). | |
| 508 | is_a_value_aux(empty_sequence). | |
| 509 | is_a_value_aux(bool_set). | |
| 510 | is_a_value_aux(max_int). | |
| 511 | is_a_value_aux(min_int). | |
| 512 | is_a_value_aux(couple(A,B)) :- is_a_value(A), is_a_value(B). | |
| 513 | % TODO: extend (records), and possibly move somewhere else |