| 1 | % (c) 2009-2022 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(kodkod_process, [register_problem/7, kodkod_request_intern/5, | |
| 6 | get_original_problem/2, | |
| 7 | clear_java_comp_time/0, | |
| 8 | get_java_comp_time/1, | |
| 9 | stop_kodkod/0 | |
| 10 | ]). | |
| 11 | ||
| 12 | :- use_module(library(avl)). | |
| 13 | :- use_module(library(lists)). | |
| 14 | :- use_module(library(process)). | |
| 15 | :- use_module(library(file_systems)). | |
| 16 | ||
| 17 | :- use_module(probsrc(module_information),[module_info/2]). | |
| 18 | :- use_module(probsrc(store), [lookup_value_for_existing_id/4]). | |
| 19 | :- use_module(probsrc(bsyntaxtree)). | |
| 20 | :- use_module(probsrc(kernel_objects), [equal_object/2]). | |
| 21 | :- use_module(probsrc(custom_explicit_sets), [expand_custom_set_to_list/2]). | |
| 22 | :- use_module(probsrc(error_manager)). | |
| 23 | :- use_module(probsrc(system_call)). | |
| 24 | :- use_module(probsrc(preferences), [get_preference/2]). | |
| 25 | :- use_module(probsrc(debug), [debug_format/3, formatsilent/2]). | |
| 26 | ||
| 27 | :- use_module(kodkodsrc(kodkod_printer)). | |
| 28 | ||
| 29 | ||
| 30 | :- module_info(group,kodkod). | |
| 31 | :- module_info(description,'This is the inter-process communication with the Java process that contains the Kodkod engine.'). | |
| 32 | ||
| 33 | :- set_prolog_flag(double_quotes, codes). | |
| 34 | ||
| 35 | :- dynamic known_problem/7, next_problem_id/1. | |
| 36 | ||
| 37 | next_problem_id(0). | |
| 38 | ||
| 39 | register_problem(Ids,KExpr,Idmap,Typemap,Valuemap,Predicate,ProblemId) :- | |
| 40 | startup_kodkod, | |
| 41 | get_new_problem_id(ProblemId), | |
| 42 | extract_all_types(Typemap,Types), | |
| 43 | extract_all_relations(Idmap,Relations), | |
| 44 | prepare_value_mapping(Valuemap,ProbToKodkodVal,KodkodToProbVal), | |
| 45 | prepare_id_mapping(Ids,Idmap,ProbToKodkodId,KodkodToProbId), | |
| 46 | assertz( known_problem(ProblemId,Types,Relations,KExpr, | |
| 47 | probkodkod(ProbToKodkodVal,ProbToKodkodId), | |
| 48 | kodkodprob(KodkodToProbVal,KodkodToProbId), | |
| 49 | Predicate) ), | |
| 50 | catch( send_problem(ProblemId), | |
| 51 | error(_,Err), % usually error(system_error,system_error('SPIO_E_NET_CONNRESET')) | |
| 52 | (format('Restarting Kodkod process due to: ~w~n',[Err]), | |
| 53 | reset_kodkod_process, | |
| 54 | startup_kodkod, | |
| 55 | send_problem(ProblemId))). | |
| 56 | ||
| 57 | get_original_problem(Id,OrigPredicate) :- | |
| 58 | known_problem(Id,_,_,_,_,_,OrigPredicate). | |
| 59 | ||
| 60 | extract_all_types(Typemap,Types) :- | |
| 61 | avl_range(Typemap,Relations), | |
| 62 | include(is_base_relation,Relations,BaseRelations), | |
| 63 | %print(extract_int_types(Relations)),nl, | |
| 64 | extract_int_types(Relations,IntTypes), | |
| 65 | %print('Result : '), print(IntTypes),nl, | |
| 66 | append(IntTypes,BaseTypes,Types), | |
| 67 | maplist(extract_type, BaseRelations, BaseTypes). | |
| 68 | ||
| 69 | is_base_relation(relation(_,_,reldef(_,_,_,_,_))). | |
| 70 | extract_type(relation(Id,Size,_Relspec),type(Id,Size)). | |
| 71 | ||
| 72 | :- use_module(kodkod_tools,[pow2integer_relation_kodkod_name/1]). | |
| 73 | extract_int_types(Relations,IntTypes) :- | |
| 74 | exclude(is_base_relation,Relations,IntRelations), | |
| 75 | extract_int_types2(IntRelations,IntTypes). | |
| 76 | extract_int_types2([],[]) :- !. | |
| 77 | extract_int_types2(IntRelations,[inttype(PowName,PowMin,PowMax,IntsetName,Min,Max)]) :- | |
| 78 | selectchk(relation(IntsetName,_,intsetrel(_Id,RMin,Max)),IntRelations,Rest),!, | |
| 79 | Min is min(RMin,0), | |
| 80 | extract_int_types2(Rest,RestResult), | |
| 81 | (RestResult = [inttype(PowName,PowMin,PowMax)] -> true | |
| 82 | ; RestResult = [] -> pow2integer_relation_kodkod_name(PowName), | |
| 83 | PowMin=Min, PowMax=Max % I have no idea if this is correct; TO DO: Daniel check this | |
| 84 | ). % What if RestResult is something else ??? | |
| 85 | extract_int_types2(IntRelations,[inttype(PowName,PowMin,PowMax)]) :- | |
| 86 | memberchk(relation(PowName,_,pow2rel(_Id,RPowMin,PowMax)),IntRelations),!, | |
| 87 | PowMin is min(RPowMin,0). | |
| 88 | ||
| 89 | prepare_id_mapping(Ids,Idmap,ProbToKodkod,KodkodToProb) :- | |
| 90 | prepare_id_mapping2(Ids,Idmap,ProbToKodkod,KodkodToProbList), | |
| 91 | list_to_avl(KodkodToProbList, KodkodToProb). | |
| 92 | prepare_id_mapping2([],_,[],[]). | |
| 93 | prepare_id_mapping2([TId|Rest],Idmap,[PK|ProbToKodkod],[KP|KodkodToProb]) :- | |
| 94 | prepare_id_mapping3(TId,Idmap,PK,KP), | |
| 95 | prepare_id_mapping2(Rest,Idmap,ProbToKodkod,KodkodToProb). | |
| 96 | prepare_id_mapping3(TId,Idmap,bid(BType,P)-K,K-bid(BType,P)) :- | |
| 97 | get_texpr_id(TId,P), | |
| 98 | get_texpr_type(TId,BType1), | |
| 99 | adapt_seq_type(BType1,BType), | |
| 100 | avl_fetch(P,Idmap,reldef(K,_Types,_SetOrSingle,_ExactOrSubset,_Set)). | |
| 101 | ||
| 102 | adapt_seq_type(seq(T),S) :- !,S=set(couple(integer,T)). | |
| 103 | adapt_seq_type(T,T). | |
| 104 | ||
| 105 | prepare_value_mapping(Valuemap,ProbToKodkod,KodkodToProb) :- | |
| 106 | avl_to_list(Valuemap,ProbKodkodList), | |
| 107 | maplist(reverse_key_value, ProbKodkodList, KodkodProbList), | |
| 108 | list_to_avl(KodkodProbList,KodkodToProb), | |
| 109 | maplist(remove_type_info,ProbKodkodList, L1), | |
| 110 | list_to_avl(L1,ProbToKodkod). | |
| 111 | reverse_key_value((T:P)-K, (T:K)-P). | |
| 112 | remove_type_info((_T:P)-K, P-K). | |
| 113 | ||
| 114 | get_new_problem_id(ProblemId) :- | |
| 115 | retract(next_problem_id(Id)), | |
| 116 | NextId is Id+1, | |
| 117 | assertz(next_problem_id(NextId)), | |
| 118 | ProblemId = Id. | |
| 119 | ||
| 120 | extract_all_relations(Idmap,Relations) :- avl_range(Idmap,Relations). | |
| 121 | ||
| 122 | send_problem(Id) :- | |
| 123 | get_kodkod_process(_,KOut,_), | |
| 124 | known_problem(Id,Types,Relations,Formula,_ToKodkod,_ToProB,_OrigPredicate), | |
| 125 | get_preference(time_out,TO), | |
| 126 | get_preference(kodkod_symmetry_level,Symmetry), | |
| 127 | get_preference(kodkod_sat_solver,Solver), | |
| 128 | kodkod_printer:kkp_problem(KOut,TO,Symmetry,Solver,Id,problem(Types,Relations,Formula,xxx,none)). | |
| 129 | ||
| 130 | get_kodkod_process(A,B,C) :- kodkod_process(A,B,C),!. | |
| 131 | get_kodkod_process(_,_,_) :- add_error(kodkod_process,'No Kodkod process running'),fail. | |
| 132 | ||
| 133 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
| 134 | % communication with external java process | |
| 135 | :- dynamic kodkod_process/3. | |
| 136 | ||
| 137 | reset_kodkod_process :- retractall(kodkod_process(_,_,_)). | |
| 138 | ||
| 139 | startup_kodkod :- kodkod_process(_,_,_), !. | |
| 140 | startup_kodkod :- | |
| 141 | absolute_file_name(prob_lib('.'), KodkodDir), | |
| 142 | kodkod_jar(KodkodJar), | |
| 143 | ||
| 144 | atom_concat('-Djava.library.path=', KodkodDir, JLibpath), | |
| 145 | ||
| 146 | get_command_path('java',CmdPath), | |
| 147 | ||
| 148 | %format('Starting Kodkod JAR ~w using ~w~n',[KodkodJar,JLibpath]), | |
| 149 | get_preference(kodkod_sat_solver,Solver), | |
| 150 | ||
| 151 | process_create(CmdPath, | |
| 152 | [JLibpath, '-Xmx1024M','-jar', KodkodJar,Solver], | |
| 153 | [process(KProc),stdout(pipe(KIn)),stdin(pipe(KOut))]), | |
| 154 | check_correct_startup(KIn), | |
| 155 | ||
| 156 | assertz(kodkod_process(KProc,KOut,KIn)). | |
| 157 | ||
| 158 | check_correct_startup(S) :- | |
| 159 | catch( (read_line(S,Line),check_first_line(Line)), | |
| 160 | E, | |
| 161 | add_error_and_fail(kodkod_process, 'Exception while connecting to Kodkod process', E)). | |
| 162 | check_first_line(Line) :- | |
| 163 | append("ProB-Kodkod started: ",Msg,Line),!, | |
| 164 | format('Kodkod module started up successfully (SAT solver ~s).~n',[Msg]). | |
| 165 | check_first_line(_Line) :- | |
| 166 | add_error_and_fail(kodkod_process,'Unable to start-up Kodkod process.\n'). | |
| 167 | ||
| 168 | kodkod_jar(KodkodJar) :- | |
| 169 | absolute_file_name(prob_lib('probkodkod.jar'), KodkodJar), | |
| 170 | ( file_exists(KodkodJar,read) -> true | |
| 171 | ; | |
| 172 | add_error_and_fail(kodkod_process,'Unable to read Kodkod library. Please check your installation or disable the use of Kodkod')). | |
| 173 | ||
| 174 | stop_kodkod :- | |
| 175 | get_kodkod_process(PId,KOut,KIn), | |
| 176 | call_cleanup( (write(KOut, 'stop.\n'), flush_output(KOut) ), | |
| 177 | ( process_wait(PId,_), close(KOut), close(KIn), | |
| 178 | retractall(kodkod_process(_,_,_)))). | |
| 179 | ||
| 180 | %kodkod_problem(Id,Problem) :- | |
| 181 | % kodkod_process(_,KOut,_), | |
| 182 | % get_preference(time_out,TO),get_preference(kodkod_symmetry_level,Symmetry),get_preference(kodkod_sat_solver,Solver), | |
| 183 | % kkp_problem(KOut,TO,Symmetry,Solver,Id,Problem). | |
| 184 | ||
| 185 | kodkod_request_intern(ProblemId, Signum, MaxNrOfSols, LocalState, State) :- | |
| 186 | known_problem(ProblemId,_Size,_Relations,_Formula,ToKodkod,ToProB,_OrigPredicate), | |
| 187 | extract_known_values(ToKodkod,LocalState,State,Known,UnknownList), | |
| 188 | copy_term(UnknownList,UnknownListCopy), | |
| 189 | list_to_avl(UnknownListCopy,Unknown), | |
| 190 | !, | |
| 191 | debug_println(19,send_kodkod_request(ProblemId,Signum,MaxNrOfSols,Known)), | |
| 192 | ? | send_request(ProblemId, ToProB, Signum, MaxNrOfSols, Known, Unknown), |
| 193 | %describe_sol(UnknownListCopy), | |
| 194 | UnknownList=UnknownListCopy. | |
| 195 | %translate:print_bstate(State). | |
| 196 | ||
| 197 | %describe_sol(List) :- | |
| 198 | % nl,write('Kodkod solution:'),nl,describe_sol2(List). | |
| 199 | %describe_sol2([]). | |
| 200 | %describe_sol2([Id-Value|Rest]) :- | |
| 201 | % describe_sol3(Id,Value), | |
| 202 | % describe_sol2(Rest). | |
| 203 | %describe_sol3(Id,Value) :- | |
| 204 | % write(Id),write(': '),translate:print_bvalue(Value),nl. | |
| 205 | ||
| 206 | ||
| 207 | extract_known_values(probkodkod(ValueMap,Ids),LocalState,State,Out,Unknown) :- | |
| 208 | extract_known_values2(Ids,ValueMap,LocalState,State,Out,[],Unknown,[]). | |
| 209 | extract_known_values2([],_ValueMap,_LocalState,_State,Known,Known,Unknown,Unknown). | |
| 210 | extract_known_values2([bid(Type,Prob)-Kodkod|Rest],ValueMap,LocalState,State, | |
| 211 | KnownIn,KnownOut,UnknownIn,UnknownOut) :- | |
| 212 | extract_known_value(Type,Prob,Kodkod,ValueMap,LocalState,State,KnownIn,KnownInter,UnknownIn,UnknownInter), | |
| 213 | extract_known_values2(Rest,ValueMap,LocalState,State,KnownInter,KnownOut,UnknownInter,UnknownOut). | |
| 214 | extract_known_value(Type,Prob,Kodkod,ValueMap,LocalState,State,KnownIn,KnownOut,UnknownIn,UnknownOut) :- | |
| 215 | lookup_value_for_existing_id(Prob,LocalState,State,Value), | |
| 216 | ( ground(Value) -> | |
| 217 | map_prob_value_to_kodkod(Type,Value,ValueMap,KValue), | |
| 218 | KnownIn = [kout(Kodkod,KValue)|KnownOut], | |
| 219 | UnknownIn = UnknownOut | |
| 220 | ; | |
| 221 | KnownIn = KnownOut, | |
| 222 | UnknownIn = [Kodkod-Value|UnknownOut]),!. | |
| 223 | extract_known_value(_Type,Prob,_Kodkod,_ValueMap,_LocalState,_State,_KnownIn,_KnownOut,_UnknownIn,_UnknownOut) :- | |
| 224 | add_error_and_fail(kodkod_process,'mapping ProB value to Kodkod failed', Prob). | |
| 225 | ||
| 226 | map_prob_value_to_kodkod(set(_),Prob,ValueMap,Kodkod) :- | |
| 227 | !, expand_custom_set_to_list(Prob,List), | |
| 228 | map_prob_values_to_kodkod(List,ValueMap,Kodkod). | |
| 229 | map_prob_value_to_kodkod(_Type,Prob,ValueMap,Kodkod) :- | |
| 230 | map_prob_values_to_kodkod([Prob],ValueMap,Kodkod). | |
| 231 | map_prob_values_to_kodkod([],_,[]). | |
| 232 | map_prob_values_to_kodkod([P|Prest],ValueMap,[tuple(K)|Krest]) :- | |
| 233 | map_prob_value_to_kodkod2(P,ValueMap,K,[]), | |
| 234 | map_prob_values_to_kodkod(Prest,ValueMap,Krest). | |
| 235 | map_prob_value_to_kodkod2((A,B),ValueMap) --> | |
| 236 | !,map_prob_value_to_kodkod2(A,ValueMap), | |
| 237 | map_prob_value_to_kodkod2(B,ValueMap). | |
| 238 | map_prob_value_to_kodkod2(int(I),_ValueMap) --> !,[I]. | |
| 239 | map_prob_value_to_kodkod2(Prob,ValueMap) --> [Kodkod], | |
| 240 | {avl_fetch(Prob,ValueMap,Kodkod)}. | |
| 241 | ||
| 242 | ||
| 243 | :- use_module(probsrc(debug),[debug_println/2]). | |
| 244 | send_request(ProblemId, ToProb, Signum, MaxNrOfSols,OutValues, In) :- | |
| 245 | get_kodkod_process(_,OStream,IStream), | |
| 246 | debug:debug_println(19,sending_kodkod_request(ProblemId,Signum,MaxNrOfSols)), | |
| 247 | kkp_request(ProblemId, Signum, MaxNrOfSols, OutValues, OStream), | |
| 248 | ? | get_solutions_from_stream(OStream,IStream,ProblemId,MaxNrOfSols,ToProb,In). |
| 249 | ||
| 250 | read_rest(ProblemId,MaxNrOfSols,ToProb,In) :- | |
| 251 | get_kodkod_process(_,OStream,IStream), | |
| 252 | kkp_list(ProblemId,MaxNrOfSols,OStream), | |
| 253 | get_solutions_from_stream(OStream,IStream,ProblemId,MaxNrOfSols,ToProb,In). | |
| 254 | ||
| 255 | get_solutions_from_stream(OStream,IStream,ProblemId,MaxNrOfSols,ToProB,In) :- | |
| 256 | flush_output(OStream), | |
| 257 | read_answer_from_kodkod(IStream,Answer), | |
| 258 | !, | |
| 259 | ? | process_answer(Answer,ProblemId,MaxNrOfSols,ToProB,In). |
| 260 | get_solutions_from_stream(_,_,ProblemId,MaxNrOfSols,ToProB,In) :- | |
| 261 | process_answer(end_of_file,ProblemId,MaxNrOfSols,ToProB,In). | |
| 262 | ||
| 263 | % read an answer but skip over statistics infos: | |
| 264 | read_answer_from_kodkod(IStream,Answer) :- | |
| 265 | read(IStream,Answer1), | |
| 266 | (Answer1=stats(T,S,C,V,P) % special statistics info fact | |
| 267 | -> debug_format(20,'Kodkod Statistics: ~w ms translation, ~w ms solving, ~w clauses, ~w variables, ~w primary variables~n',[T,S,C,V,P]), | |
| 268 | read_answer_from_kodkod(IStream,Answer) | |
| 269 | ; Answer=Answer1). | |
| 270 | ||
| 271 | process_answer(solutions(Sols,More,Times),ProblemId,MaxNrOfSols,ToProb,In) :- | |
| 272 | formatsilent('Times for computing solutions: ~w~n',[Times]), | |
| 273 | save_java_comp_time(Times), | |
| 274 | ? | get_solutions(Sols,More,ProblemId,MaxNrOfSols,ToProb,In). |
| 275 | process_answer(sat_timeout,ProblemId,_,_ToProb,_In) :- | |
| 276 | add_warning(kodkod_process,'Kodkod SAT Solver Timeout for Problem Id: ',ProblemId), | |
| 277 | %throw(time_out). % can only be caught by time_out not by catch ! | |
| 278 | throw(enumeration_warning(kodkod_timeout,ProblemId,'','',critical)). | |
| 279 | process_answer(sat_non_incremental,ProblemId,_,_ToProb,_In) :- | |
| 280 | add_warning(kodkod_process,'Kodkod SAT Solver is non incremental and cannot compute all solutions for Problem Id: ',ProblemId), | |
| 281 | throw(enumeration_warning(kodkod_timeout,ProblemId,'','',critical)). | |
| 282 | process_answer(unknown(_),ProblemId,_,_ToProb,_In) :- | |
| 283 | add_warning(kodkod_process,'Kodkod SAT Solver error occured for Problem Id: ',ProblemId), | |
| 284 | throw(enumeration_warning(kodkod_error,ProblemId,'','',critical)). | |
| 285 | process_answer(end_of_file,ProblemId,_,_ToProb,_In) :- | |
| 286 | add_warning(kodkod_process,'Kodkod SAT Solver aborted for Problem Id: ',ProblemId), | |
| 287 | throw(enumeration_warning(kodkod_error,ProblemId,'','',critical)). | |
| 288 | ||
| 289 | ||
| 290 | get_solutions(Sols,_More,_ProblemId,_,ToProb,In) :- | |
| 291 | member(Sol,Sols), get_solution(Sol,ToProb,In). | |
| 292 | get_solutions(_Sols,more,ProblemId,MaxNrOfSols,ToProb,In) :- | |
| 293 | read_rest(ProblemId,MaxNrOfSols,ToProb,In). | |
| 294 | ||
| 295 | get_solution([],_,_). | |
| 296 | get_solution([Binding|BRest],kodkodprob(VMap,IdMap),BValues) :- | |
| 297 | ( Binding = b(Name,KValues) -> true | |
| 298 | ; Binding = s(Name,KValues) -> Type = set(_)), | |
| 299 | avl_fetch(Name,BValues,BValue), | |
| 300 | avl_fetch(Name,IdMap,bid(Type,_BName)), | |
| 301 | map_kodkod_prob(Type,KValues,VMap,Result),!, | |
| 302 | equal_object(BValue,Result), | |
| 303 | get_solution(BRest,kodkodprob(VMap,IdMap),BValues). | |
| 304 | ||
| 305 | map_kodkod_prob(set(Type),Tuples,Map,Value) :- | |
| 306 | !,map_kodkod_prob2(Tuples,Type,Map,Value). | |
| 307 | map_kodkod_prob(Type,KValue,Map,BValue) :- | |
| 308 | map_kodkod_prob2([KValue],Type,Map,[BValue]). | |
| 309 | map_kodkod_prob2([],_,_,[]). | |
| 310 | map_kodkod_prob2([Values|TRest],Type,Map,[BValue|BRest]) :- | |
| 311 | map_kodkod_prob3(Type,Map,BValue,Values,[]), | |
| 312 | map_kodkod_prob2(TRest,Type,Map,BRest). | |
| 313 | map_kodkod_prob3(couple(TA,TB),Map,(BA,BB)) --> | |
| 314 | map_kodkod_prob3(TA,Map,BA), | |
| 315 | map_kodkod_prob3(TB,Map,BB). | |
| 316 | map_kodkod_prob3(integer,_,int(Value)) --> !,[Value]. | |
| 317 | map_kodkod_prob3(Type,Map,BValue) --> | |
| 318 | [K],{avl_fetch(Type:K,Map,BValue),!}. | |
| 319 | ||
| 320 | :- dynamic java_comp_time/1. | |
| 321 | clear_java_comp_time :- | |
| 322 | retractall(java_comp_time(_)). | |
| 323 | get_java_comp_time(Time) :- | |
| 324 | findall( T, (java_comp_time(Ts),member(T,Ts)), Times), | |
| 325 | sumlist(Times,Time). | |
| 326 | save_java_comp_time(Times) :- | |
| 327 | assertz(java_comp_time(Times)). |