| 1 | % (c) 2009-2024 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
| 2 | % Heinrich Heine Universitaet Duesseldorf | |
| 3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
| 4 | ||
| 5 | ||
| 6 | :- module(kodkod,[replace_by_kodkod/3, | |
| 7 | current_solver_is_not_incremental/0, | |
| 8 | kodkod_request/5, | |
| 9 | %apply_kodkod_to_counterexample_search/5, | |
| 10 | kodkod_reset/0]). | |
| 11 | ||
| 12 | :- use_module(library(lists)). | |
| 13 | :- use_module(library(avl)). | |
| 14 | ||
| 15 | :- use_module(probsrc(tools)). | |
| 16 | :- use_module(probsrc(bmachine)). | |
| 17 | :- use_module(probsrc(self_check)). | |
| 18 | :- use_module(probsrc(bsyntaxtree)). | |
| 19 | :- use_module(probsrc(translate)). | |
| 20 | :- use_module(probsrc(btypechecker)). | |
| 21 | :- use_module(probsrc(b_global_sets),[b_global_set/1,lookup_global_constant/2]). | |
| 22 | :- use_module(probsrc(preferences),[get_preference/2]). | |
| 23 | ||
| 24 | :- use_module(kodkodsrc(predicate_analysis)). | |
| 25 | :- use_module(kodkodsrc(kodkod_translate)). | |
| 26 | :- use_module(kodkodsrc(kodkod_tools)). | |
| 27 | :- use_module(kodkodsrc(kodkod_rewrite)). | |
| 28 | :- use_module(kodkodsrc(kodkod2)). | |
| 29 | :- use_module(kodkodsrc(kodkod_typing)). | |
| 30 | :- use_module(kodkodsrc(kodkod_process)). | |
| 31 | :- use_module(kodkodsrc(kodkod_annotator)). | |
| 32 | ||
| 33 | :- use_module(probsrc(module_information),[module_info/2]). | |
| 34 | :- module_info(group,kodkod). | |
| 35 | :- module_info(description,'This is the central module of the B to Kodkod translation (still experimental).'). | |
| 36 | ||
| 37 | %:- dynamic kodkod_error/1. | |
| 38 | ||
| 39 | kodkod_reset :- | |
| 40 | ( stop_kodkod -> true; true). | |
| 41 | ||
| 42 | % Signum is either pos (when we want the predicate to succeed) or neg (when we want the predicate to be false) | |
| 43 | kodkod_request(Id,Signum,LocalState,State,LWF) :- | |
| 44 | % MaxNrOfSols is the maximum number of solutions found in one go by Kodkod | |
| 45 | get_max_nr_of_solutions(MaxNrOfSols), | |
| 46 | kodkod_request(Id,Signum,MaxNrOfSols,LocalState,State,LWF). | |
| 47 | ||
| 48 | get_max_nr_of_solutions(MaxNrOfSols) :- | |
| 49 | current_solver_is_not_incremental,!, | |
| 50 | MaxNrOfSols = 1. | |
| 51 | get_max_nr_of_solutions(MaxNrOfSols) :- | |
| 52 | get_preference(kodkod_max_nr_solutions,MaxNrOfSols). % TO DO: we could look at kodkod_for_components and maxNrOfInitialisations preference | |
| 53 | ||
| 54 | current_solver_is_not_incremental :- | |
| 55 | get_preference(kodkod_sat_solver,Solver), | |
| 56 | not_incremental(Solver). | |
| 57 | not_incremental(lingeling). | |
| 58 | ||
| 59 | :- use_module(probsrc(error_manager)). | |
| 60 | ||
| 61 | :- block kodkod_request(?,?,?,?,?,-). | |
| 62 | kodkod_request(Id,Signum,MaxNrOfSols,LocalState,State,_LWF) :- | |
| 63 | ? | catch( |
| 64 | kodkod_request_intern(Id,Signum,MaxNrOfSols,LocalState,State), | |
| 65 | error(system_error,system_error(E)), % often SPIO_E_NET_CONNRESET | |
| 66 | (add_internal_error('System Error while processing Kodkod request: ',E), fail)). | |
| 67 | ||
| 68 | ||
| 69 | % | |
| 70 | :- use_module(extrasrc(b_expression_sharing),[expand_cse_lazy_lets/2]). | |
| 71 | replace_by_kodkod(Constants,Predicate,NewPredicate) :- | |
| 72 | % Remove any CSE lazy_lets as Kodkod translation cannot deal with them yet | |
| 73 | (get_preference(use_common_subexpression_elimination,true) -> | |
| 74 | expand_cse_lazy_lets(Predicate,Predicate0) | |
| 75 | ; Predicate0=Predicate), | |
| 76 | % The Predicate may already contain Kodkod calls, replace | |
| 77 | % them by their original B predicate | |
| 78 | undo_kodkod_replacements(Predicate0,Predicate1), | |
| 79 | % first we do a predicate analysis to determine ranges for integers | |
| 80 | try_predicate_analysis_with_global_sets(Predicate1,Constants,TaggedConstants,TaggedPredicate), | |
| 81 | replace_by_kodkod2(TaggedConstants,TaggedPredicate,NewPredicate). | |
| 82 | ||
| 83 | :- use_module(probsrc(preferences),[temporary_set_preference/3,reset_temporary_preference/2]). | |
| 84 | replace_by_kodkod2(Constants,_Predicate,NewPredicate) :- | |
| 85 | % If the static analysis states that there is no possible solution for one | |
| 86 | % of the identifiers, we just say "false" | |
| 87 | somechk(is_inconsistent_expression,Constants),!, | |
| 88 | write('kodkod: Static analysis detected inconsistencies, new predicate is "false"'),nl, | |
| 89 | create_texpr(falsity,pred,[],NewPredicate). | |
| 90 | replace_by_kodkod2(Constants,Predicate,NewPredicate) :- | |
| 91 | temporary_set_preference(partition_predicates_inline_equalities,false,CHNG), | |
| 92 | split_predicates(Predicate,PList), % will split into components if KODKOD_ONLY_FULL = FALSE | |
| 93 | reset_temporary_preference(partition_predicates_inline_equalities,CHNG), | |
| 94 | % we try to translate each predicate alone | |
| 95 | try_apply_transformation_l(PList,Constants,1,Result), | |
| 96 | print_results(Result), | |
| 97 | % split the result into two lists: the successfull translatable predicates (ToKodkod) | |
| 98 | % and those which were not translatable (StayProb) | |
| 99 | filter_translatable(Result,ToKodkod,StayProb), | |
| 100 | %%print('Stay ProB: '), conjunct_predicates(StayProb,StayPred), translate:print_bexpr(StayPred),nl, | |
| 101 | % fail if there are no translatable predicates | |
| 102 | ToKodkod = [_|_], | |
| 103 | % now, translate all translatable predicates in one run | |
| 104 | (StayProb = [_|_] -> add_message(kodkod,'Not translating all components/predicates with Kodkod') ; true ), | |
| 105 | % with a special preference to force full evaluation we could also abort here and return UNKNOWN | |
| 106 | apply_transformation(Constants,ToKodkod,KPred), | |
| 107 | % the new | |
| 108 | %%print('For Kodkod: '), translate:print_bexpr(KPred),nl, | |
| 109 | conjunct_predicates([KPred|StayProb],NewPredicate). | |
| 110 | ||
| 111 | ||
| 112 | split_predicates(Predicate,Parts) :- | |
| 113 | get_preference(kodkod_for_components,false),!, | |
| 114 | conjunction_to_list(Predicate,Parts). | |
| 115 | split_predicates(Predicate,Parts) :- | |
| 116 | % only translate full components (or component not at all) | |
| 117 | predicate_components(Predicate,Components), | |
| 118 | findall(P,member(component(P,_),Components),Parts). | |
| 119 | ||
| 120 | split_simple_equalities(InPredicate,Equalities,Rest) :- | |
| 121 | conjunction_to_list(InPredicate,InPredicates), | |
| 122 | split_list(is_simple_equality,InPredicates,EqualitiesL,RestL), | |
| 123 | conjunct_predicates(EqualitiesL,Equalities), | |
| 124 | conjunct_predicates(RestL,Rest). | |
| 125 | ||
| 126 | is_simple_equality(TExpr) :- | |
| 127 | get_texpr_expr(TExpr,equal(A,B)), | |
| 128 | ( get_texpr_id(A,_),is_base_expression(B) | |
| 129 | ; get_texpr_id(B,_),is_base_expression(A)),!. | |
| 130 | ||
| 131 | is_base_expression(TExpr) :- | |
| 132 | get_texpr_expr(TExpr,Expr), | |
| 133 | is_base_expression2(Expr). | |
| 134 | is_base_expression2(identifier(Id)) :- b_global_set(Id),!. % a deferred set | |
| 135 | is_base_expression2(identifier(Id)) :- lookup_global_constant(Id,_). % an enumerated set element | |
| 136 | is_base_expression2(integer(_)). | |
| 137 | is_base_expression2(couple(A,B)) :- is_base_expression(A),is_base_expression(B). | |
| 138 | is_base_expression2(set_extension(L)) :- maplist(is_base_expression,L). | |
| 139 | is_base_expression2(value(_)). | |
| 140 | is_base_expression2(boolean_true). | |
| 141 | is_base_expression2(boolean_false). | |
| 142 | is_base_expression2(empty_set). | |
| 143 | is_base_expression2(bool_set). | |
| 144 | is_base_expression2(add(A,B)) :- is_base_expression(A),is_base_expression(B). | |
| 145 | is_base_expression2(minus(A,B)) :- is_base_expression(A),is_base_expression(B). | |
| 146 | is_base_expression2(unary_minus(A)) :- is_base_expression(A). | |
| 147 | is_base_expression2(multiplication(A,B)) :- is_base_expression(A),is_base_expression(B). | |
| 148 | is_base_expression2(div(A,B)) :- is_base_expression(A),is_base_expression(B). | |
| 149 | is_base_expression2(floored_div(A,B)) :- is_base_expression(A),is_base_expression(B). | |
| 150 | is_base_expression2(modulo(A,B)) :- is_base_expression(A),is_base_expression(B). | |
| 151 | is_base_expression2(power_of(A,B)) :- is_base_expression(A),is_base_expression(B). | |
| 152 | ||
| 153 | ||
| 154 | undo_kodkod_replacements(Old,New) :- | |
| 155 | contains_kodkod_call(Old),!, | |
| 156 | undo_kodkod_replacements2(Old,New). | |
| 157 | undo_kodkod_replacements(Old,Old). | |
| 158 | undo_kodkod_replacements2(TOld,TNew) :- | |
| 159 | get_texpr_expr(TOld,Old), | |
| 160 | undo_kodkod_replacements3(Old,TOld,TNew). | |
| 161 | undo_kodkod_replacements3(conjunct(A,B),_TOld,TNew) :- | |
| 162 | !,create_texpr(conjunct(NewA,NewB),pred,[],TNew), | |
| 163 | undo_kodkod_replacements2(A,NewA), | |
| 164 | undo_kodkod_replacements2(B,NewB). | |
| 165 | undo_kodkod_replacements3(kodkod(Id,_),_TOld,Orig) :- | |
| 166 | !,get_original_problem(Id,Orig). | |
| 167 | undo_kodkod_replacements3(_Old,TOld,TOld). | |
| 168 | ||
| 169 | contains_kodkod_call(TExpr) :- | |
| 170 | get_texpr_expr(TExpr,Expr), | |
| 171 | contains_kodkod_call2(Expr),!. | |
| 172 | contains_kodkod_call2(kodkod(_,_)). | |
| 173 | contains_kodkod_call2(conjunct(A,_)) :- contains_kodkod_call(A). | |
| 174 | contains_kodkod_call2(conjunct(_,B)) :- contains_kodkod_call(B). | |
| 175 | ||
| 176 | ||
| 177 | try_predicate_analysis_with_global_sets(Predicate,Constants,TaggedConstants,TaggedPredicate) :- | |
| 178 | ? | catch(predicate_analysis_with_global_sets(Predicate,[],Constants,TaggedConstants,TaggedPredicate), |
| 179 | failed_analysis(Expr), | |
| 180 | ( write('kodkod fail: predicate analysis failed on expression '), | |
| 181 | translate_bexpression_with_limit(Expr,80,Msg), | |
| 182 | write(Msg),nl,fail)), | |
| 183 | !. | |
| 184 | try_predicate_analysis_with_global_sets(_Predicate,_Constants,_TaggedConstants,_TaggedPredicate) :- | |
| 185 | write('kodkod fail: predicate analysis failed.\n'), | |
| 186 | fail. | |
| 187 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
| 188 | ||
| 189 | :- meta_predicate try_call(-,-,0). | |
| 190 | ||
| 191 | try_apply_transformation(Predicate,Constants,Index,Result) :- | |
| 192 | %%print('TRY KODKOD ON: '), translate:print_bexpr(Predicate),nl, | |
| 193 | Log = log(Predicate,Result,Index,Ints,Bits), | |
| 194 | try_call( 'applying rewriting rules', Log, | |
| 195 | apply_rewriting_rules(Predicate,Rewritten)), | |
| 196 | %%print('REWRITTEN: '), translate:print_bexpr(Rewritten),nl, | |
| 197 | try_call( 'extracting used types', Log, | |
| 198 | extract_used_types(Rewritten,Types,Ints,Bits)), | |
| 199 | try_call( 'creating Kodkod atoms for types', Log, | |
| 200 | create_atoms_for_types(Types,Ints,Bits,Typemap,Idmap1,_Valuemap)), | |
| 201 | try_call( 'translate and type Kodkod formula', Log, | |
| 202 | typed_translation(Constants,Predicate,Bits,Typemap,Idmap1,_Idmap,Rewritten,_KExpr)), | |
| 203 | finalize_result(Log). | |
| 204 | try_call(_Msg,log(_Predicate,Result,_Index,_,_),_Call) :- | |
| 205 | nonvar(Result),!. | |
| 206 | try_call(Msg,log(Predicate,Result,Index,_,_),Call) :- | |
| 207 | %%format('Try ~w ~n',[Msg]), | |
| 208 | ( catch(Call, | |
| 209 | kodkod(T), | |
| 210 | ( %%format('Exception: ~w~n',[T]), | |
| 211 | Result = failure(Index,Predicate,Msg,kodkod_exception(T))) | |
| 212 | ) -> true | |
| 213 | ; | |
| 214 | %%format('Failure for: ~w~n',[Msg]), | |
| 215 | Result = failure(Index,Predicate,Msg,failed) | |
| 216 | ). | |
| 217 | finalize_result(log(Predicate,Result,Index,Ints,Bits)) :- | |
| 218 | var(Result),!,Result=ok(Index,Predicate,Ints,Bits). | |
| 219 | finalize_result(_). | |
| 220 | ||
| 221 | ||
| 222 | typed_translation(Constants,Predicate,Bits,Typemap,Idmap1,Idmap,Rewritten,KExpr) :- | |
| 223 | % Create the relations for the constants that are actually used | |
| 224 | restrict_to_used_constants(Constants,Predicate,UsedConstants), | |
| 225 | create_identifier_relations(UsedConstants,Typemap,Bits,Idmap1,Idmap), | |
| 226 | % translate the formula in two steps: | |
| 227 | kodkod_translate(Rewritten,Typemap,Idmap,Formula), | |
| 228 | kodkod_insert_casts(Formula,Typemap,Idmap,KExpr), | |
| 229 | !. | |
| 230 | ||
| 231 | restrict_to_used_constants(Constants,Predicate,UsedConstants) :- | |
| 232 | find_identifier_uses(Predicate,['$examine_value_was_identifier_info'],UsedIds), % flag says: also get value(_) with was_identifier(_) field | |
| 233 | include(constant_is_used(UsedIds),Constants,UsedConstants). | |
| 234 | constant_is_used(UsedIds,C) :- | |
| 235 | get_texpr_id(C,Id),memberchk(Id,UsedIds). | |
| 236 | ||
| 237 | try_apply_transformation_l([],_Constants,_,[]). | |
| 238 | try_apply_transformation_l([Predicate|Prest],Constants,I,[Result|Rrest]) :- | |
| 239 | try_apply_transformation(Predicate,Constants,I,Result), | |
| 240 | I2 is I+1, | |
| 241 | try_apply_transformation_l(Prest,Constants,I2,Rrest). | |
| 242 | ||
| 243 | ||
| 244 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
| 245 | ||
| 246 | filter_translatable([],[],[]). | |
| 247 | filter_translatable([Res|Rrest],ToKodkod,StayProb) :- | |
| 248 | ( Res = ok(_,P,_,_) -> ToKodkod=[P|Krest], StayProb=Prest | |
| 249 | ; Res = failure(_,P,_,_) -> ToKodkod=Krest, StayProb=[P|Prest]), | |
| 250 | filter_translatable(Rrest,Krest,Prest). | |
| 251 | ||
| 252 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
| 253 | :- use_module(probsrc(error_manager),[add_warning/3,add_warning/4]). | |
| 254 | :- use_module(probsrc(debug),[debug_mode/1,silent_mode/1,nls/0,debug_println/2]). | |
| 255 | ||
| 256 | apply_transformation(AllConstants,PredicateList,Result) :- | |
| 257 | conjunct_predicates(PredicateList,Predicate1), | |
| 258 | (debug_mode(on) -> print('TRANSLATING FOR KODKOD: '),nl,translate:print_bexpr_with_limit(Predicate1,1500),nl ; true), | |
| 259 | apply_transformation2(AllConstants,Predicate1,Result). | |
| 260 | ||
| 261 | apply_transformation2(AllConstants,Predicate1,Result) :- | |
| 262 | split_simple_equalities(Predicate1,Equalities,Predicate), | |
| 263 | (debug_mode(on) -> print('Equalities: '), translate:print_bexpr_with_limit(Equalities,1500),nl ; true), | |
| 264 | (is_truth(Predicate) | |
| 265 | -> debug_println(19,'Predicate for Kodkod has only equalities. Predicate will be solved by ProB.'), | |
| 266 | Result=Predicate1 | |
| 267 | % we could also fail, but then we would create a kodkod_fail warning | |
| 268 | ; apply_transformation3(AllConstants,Predicate,Equalities,Result) | |
| 269 | -> (debug_mode(on) -> print('RESULT: '),nl,translate:print_bexpr_with_limit(Result,1500),nl ; true) | |
| 270 | ; add_warning(kodkod,'Kodkod Transformation Failed for Predicate over: ',AllConstants), | |
| 271 | %trace, apply_transformation3(AllConstants,Predicate,Equalities,Result), | |
| 272 | fail). | |
| 273 | ||
| 274 | apply_transformation3(AllConstants,Predicate,Equalities,Result) :- | |
| 275 | restrict_to_used_constants(AllConstants,Predicate,Constants), | |
| 276 | apply_rewriting_rules(Predicate,Rewritten), | |
| 277 | extract_used_types(Rewritten,Types,Ints,Bits), | |
| 278 | create_atoms_for_types(Types,Ints,Bits,Typemap,Idmap1,Valuemap), | |
| 279 | % check if any types are used, if not Kodkod would exit with an | |
| 280 | % empty universe exception | |
| 281 | % (java.lang.IllegalArgumentException: Cannot create an empty universe. or something similar) | |
| 282 | \+ empty_avl(Typemap), | |
| 283 | typed_translation(Constants,Predicate,Bits,Typemap,Idmap1,Idmap,Rewritten,KExpr), | |
| 284 | ||
| 285 | register_problem(Constants,KExpr,Idmap,Typemap,Valuemap,Predicate,ProblemNr), | |
| 286 | create_texpr(kodkod(ProblemNr,Constants),pred,[],KPred), | |
| 287 | conjunct_predicates([Equalities,KPred],Result). | |
| 288 | ||
| 289 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
| 290 | ||
| 291 | print_results([]). | |
| 292 | print_results([R|Rrest]) :- | |
| 293 | print_result(R),nls, | |
| 294 | print_results(Rrest). | |
| 295 | print_result(ok(_Ids,Index,P,Ints,Bits)) :- | |
| 296 | print_result(ok(Index,P,Ints,Bits)). | |
| 297 | print_result(ok(_Index,P,Ints,Bits)) :- | |
| 298 | (silent_mode(on) -> true | |
| 299 | ; write('kodkod ok: '),print_bexpr_with_limit(P,50), | |
| 300 | write(' ints: '),write(Ints),write(', intatoms: '),write(Bits) | |
| 301 | ). | |
| 302 | print_result(failure(_Ids,Index,P,Msg,Reason)) :- | |
| 303 | print_result(failure(Index,P,Msg,Reason)). | |
| 304 | print_result(failure(_Index,P,Msg,Reason)) :- | |
| 305 | write('kodkod fail: '), | |
| 306 | translate_bexpression_with_limit(P,50,PS), write(PS), | |
| 307 | write(' , reason: '),write_reason(Reason),write(' while '),write(Msg),nl, | |
| 308 | (get_preference(kodkod_raise_warnings,false) -> true | |
| 309 | ; add_warning(kodkod,'Cannot translate predicate for Kodkod: ',PS,P) | |
| 310 | ). | |
| 311 | write_reason(kodkod_exception(KE)) :- | |
| 312 | write_reason2(KE),!. | |
| 313 | write_reason(E) :- write(E). | |
| 314 | ||
| 315 | write_reason2(unsupported_type(T)) :- | |
| 316 | pretty_type(T,PT), | |
| 317 | write('unsupported type '),write(PT). | |
| 318 | write_reason2(unsupported_expression(TE)) :- | |
| 319 | get_texpr_expr(TE,T), | |
| 320 | functor(T,Functor,Arity), | |
| 321 | translate_bexpression(TE,PE), | |
| 322 | format('unsupported expression ~w/~w: ~w',[Functor,Arity,PE]). | |
| 323 | write_reason2(unsupported_int(Msg)) :- | |
| 324 | write(Msg). | |
| 325 | write_reason2(side_condition(Msg,PPExpr,Info)) :- | |
| 326 | (extract_span_description(Info,Desc) | |
| 327 | -> format('~w: ~w (~w)',[Msg,PPExpr, Desc]) | |
| 328 | ; format('~w: ~w',[Msg,PPExpr])). | |
| 329 | ||
| 330 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
| 331 | ||
| 332 | :- dynamic user:portray/1. | |
| 333 | :- public pdebug/0. | |
| 334 | pdebug :- | |
| 335 | install_b_portray_hook, | |
| 336 | assertz(( user:portray(X) :- is_avl(X),!,portray_avl(X) )). |