1 % (c) 2009-2019 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(solver_interface, [solve_predicate/3, solve_predicate/4, solve_predicate/5,
6 set_up_typed_localstate_for_pred/4,
7 type_check_in_machine_context/2,
8 predicate_uses_unfixed_deferred_set/1, predicate_uses_unfixed_deferred_set/2,
9 unfixed_typed_id_in_list/3,
10 call_with_smt_mode_enabled/1]).
11
12 :- use_module(module_information,[module_info/2]).
13 :- module_info(group,cbc).
14 :- module_info(description,'This module provides an interface to ProB\'s constraint solving kernel.').
15
16 :- use_module(library(lists), [include/3,keys_and_values/3]).
17
18 :- use_module(bmachine, [b_get_machine_variables/1, b_get_machine_constants/1, full_b_machine/1]).
19 :- use_module(btypechecker,[env_empty/1,opentype/5, add_identifiers_to_environment/3, env_store_operator/4, env_store_definition/3]).
20 :- use_module(b_global_sets, [is_b_global_constant/3,
21 b_global_set/1, unfixed_deferred_set/1,
22 contains_unfixed_deferred_set/1]).
23 :- use_module(b_interpreter, [set_up_typed_localstate/6]).
24 :- use_module(tools_timeout, [time_out_with_factor_call/4]).
25 :- use_module(bsyntaxtree, [find_typed_identifier_uses/3, find_typed_identifier_uses/2,
26 map_over_typed_bexpr/3, get_texpr_id/2, get_texpr_type/2, get_texpr_types/2]).
27 :- use_module(error_manager).
28 :- use_module(tools,[assert_once/1,foldl/5,foldl/4]).
29 :- use_module(translate, [translate_bvalue/2, translate_bvalue_with_limit/3]).
30 :- use_module(bmachine_structure,[get_section/3]).
31 :- use_module(debug,[debug_println/2]).
32 :- use_module(preferences).
33 :- use_module(eventhandling,[announce_event/1]).
34 :- use_module(smt_solvers_interface(smt_solvers_interface)).
35 :- use_module(store, [normalise_value_for_var/4]).
36
37 :- meta_predicate call_with_smt_mode_enabled(0).
38 %:- meta_predicate call_with_chr(0).
39 %call_with_chr(Call) :- call_with_preference(Call,use_chr_solver,true).
40
41
42 % :- meta_predicate call_with_smt_mode_enabled(0). % directive above for Spider
43 call_with_smt_mode_enabled(Call) :-
44 temporary_set_preference(use_smt_mode,true,SMT),
45 temporary_set_preference(use_clpfd_solver,true,CLPFD), % we also enable CLPFD
46 % TO DO: also enable CHR
47 call_cleanup( (Call,!),
48 (reset_temporary_preference(use_smt_mode,SMT),
49 reset_temporary_preference(use_clpfd_solver,CLPFD)
50 )).
51
52 % solve predicate finds *one* solution for predicate in State
53 solve_predicate(Pred,State,Res) :- solve_predicate(Pred,State,1,Res).
54 solve_predicate(Pred,State,TimeoutFactor,Res) :-
55 solve_predicate(Pred,State,TimeoutFactor,[use_smt_mode/true,use_clpfd_solver/true],Res).
56
57 % one could add a preference to try Z3 instead/or first:
58 %solve_predicate(Pred,State,TimeoutFactor,Options,RealRes) :-
59 % % preference(solve_predicates_with_z3,true),
60 % !, print(solve_with_z3),nl, translate:print_bexpr(Pred),nl,
61 % ((TimeoutFactor=1,Options=[]) -> true
62 % ; format('TO DO; ignoring Timeout Factor ~w and Options ~w for Z3~n',[TimeoutFactor,Options])),
63 % smt_solvers_interface:smt_solve_predicate(z3,Pred,State,RealRes),
64 % print(z3_result(RealRes)),nl.
65 solve_predicate(Pred,State,TimeoutFactor,Options,RealRes) :-
66 solve_predicate_with_options(Options,[],Pred,State,TimeoutFactor,Res), !,
67 if(solver_result(Res,Pred,State,Options,RealResBeforeFallback),true,
68 (add_internal_error('Unknown solver_result: ',Res),RealResBeforeFallback=error)),
69 fallback_to_smt_if_requested(RealResBeforeFallback,Pred,State,RealRes).
70
71 % no fallback if solution / contradiction
72 fallback_to_smt_if_requested(solution(S),_,_,Res) :- !, Res=solution(S).
73 fallback_to_smt_if_requested(contradiction_found,_,_,Res) :- !,Res=contradiction_found.
74 fallback_to_smt_if_requested(S,_,_,Res) :-
75 \+ preferences:get_preference(smt_supported_interpreter,true), ! , Res = S.
76 fallback_to_smt_if_requested(_,Pred,State,Res) :-
77 smt_solve_predicate(z3,Pred,State,Res), !.
78
79 solve_predicate_with_options([],RestOptions,Pred,State,TimeoutFactor,Res) :-
80 solve_predicate_direct(Pred,State,TimeoutFactor,RestOptions,Res).
81 solve_predicate_with_options([Option|T],RestOptions,Pred,State,TimeoutFactor,Res) :-
82 solve_with_option(Option,T,RestOptions,Pred,State,TimeoutFactor,Res).
83
84 solve_with_option('CHR',T,Rest,Pred,State,TimeoutFactor,Res) :- !,
85 solve_with_option(use_chr_solver/true,T,Rest,Pred,State,TimeoutFactor,Res).
86 solve_with_option('SMT',T,Rest,Pred,State,TimeoutFactor,Res) :- !,
87 solve_with_option(use_smt_mode/true,T,Rest,Pred,State,TimeoutFactor,Res).
88 solve_with_option('KODKOD',T,Rest,Pred,State,TimeoutFactor,Res) :- !,
89 solve_with_option(try_kodkod_on_load/true,T,Rest,Pred,State,TimeoutFactor,Res).
90 solve_with_option('SMT_SUPPORTED_INTERPRETER',T,Rest,Pred,State,TimeoutFactor,Res) :- !,
91 solve_with_option(smt_supported_interpreter/true,T,Rest,Pred,State,TimeoutFactor,Res).
92 solve_with_option('CLPFD',T,Rest,Pred,State,TimeoutFactor,Res) :- !,
93 solve_with_option(use_clpfd_solver/true,T,Rest,Pred,State,TimeoutFactor,Res).
94 solve_with_option(Pref/Val,T,Rest,Pred,State,TimeoutFactor,Res) :- !,
95 call_with_preference(solve_predicate_with_options(T,Rest,Pred,State,TimeoutFactor,Res),Pref,Val).
96 solve_with_option(Opt,T,Rest,Pred,State,TimeoutFactor,Res) :-
97 valid_option(Opt),!,
98 solve_predicate_with_options(T,[Opt|Rest],Pred,State,TimeoutFactor,Res).
99 solve_with_option(Opt,T,Rest,Pred,State,TimeoutFactor,Res) :-
100 add_warning(solve_with_option,'Unrecognised option: ',Opt),
101 solve_predicate_with_options(T,Rest,Pred,State,TimeoutFactor,Res).
102
103 valid_option(full_machine_state).
104 valid_option(solve_in_visited_state(S)) :- atomic(S).
105 valid_option(truncate(Limit)) :- number(Limit). % truncate pretty printing
106 valid_option(truncate). % 10000
107 valid_option(force_evaluation). % force evaluation of symbolic results
108
109 solve_predicate_direct(Pred,State,TimeoutFactor,Options,Res) :-
110 %temporary_set_preference(randomise_enumeration_order,true),
111 %temporary_set_preference(find_abort_values,true), % I am not sure we should do this; we want to find counter-examples quickly, we are less worried with finding WD-problems ?!
112
113 %temporary_set_preference(minint,-2147483648), % disabled to pass tests 1004,1015,..
114 %temporary_set_preference(maxint,2147483647),
115 (real_error_occurred -> reset_real_error_occurred, PrevErr=true ; PrevErr=false),
116
117 smt_solvers_interface:init,
118 announce_event(start_solving),
119 solve_predicate_aux(Pred,State,TimeoutFactor,Options,InnerRes),
120 announce_event(end_solving),
121
122 (InnerRes = overflow -> Res = overflow ; real_error_occurred -> Res = error; Res = InnerRes),
123 (PrevErr -> assert_real_error_occurred ; true). % re-establish real-error occured
124
125 %:- use_module(clpfd_interface,[clpfd_overflow_error_message/0]).
126 solve_predicate_aux(Pred,State,TimeoutFactor,Options,Res) :-
127 enter_new_error_scope(ScopeID,solve_predicate), %print(enter_scope(ScopeID)),nl,
128 call_cleanup(solve_predicate_aux1(Pred,State,TimeoutFactor,Options,Res),
129 exit_error_scope(ScopeID,_,solve_predicate_aux)).
130
131 solve_predicate_aux1(Pred,State,TimeoutFactor,Options,Res) :-
132 solve_predicate_aux2(Pred,State,TimeoutFactor,Options,Res),
133 !, % we only look for one solution
134 clear_enumeration_warnings. % avoid propagating them higher up
135 solve_predicate_aux1(_Pred,State,_TimeoutFactor,_Options,Res) :-
136 critical_enumeration_warning_occured_in_error_scope(A,B,C,D), % we did not find a solution: check for enum warnings
137 !,
138 clear_enumeration_warnings,
139 State = no_state,
140 Res = virtual_time_out(enumeration_warning(A,B,C,D,critical)).
141 solve_predicate_aux1(_Pred,State,_TimeoutFactor,_Options,Res) :-
142 State = no_state,
143 Res = no.
144
145
146 :- use_module(clpfd_interface,[catch_clpfd_overflow_call2/2]).
147 solve_predicate_aux2(Pred,State,TimeoutFactor,Options,Res) :-
148 %predicate_level_optimizations(Pred,OptPred), % is now done in b_interpreter_components; to avoid that CSE disrupts partitioning
149 catch_clpfd_overflow_call2(
150 solve_predicate_aux3(Pred,State,TimeoutFactor,Options,Res),
151 ( %print(overflow),nl,trace,
152 %clpfd_interface:clpfd_overflow_warning_message, % message is already printed by catch_clpfd_overflow_call2
153 % we could try and solve without CLPFD ?!!
154 Res=overflow,State=no_state)).
155
156 /*
157 filter_global_identifiers([],[]).
158 filter_global_identifiers([Identifier|IDs],Out) :-
159 filter_global_identifiers(IDs,TempOut),
160 (is_global(Identifier)
161 -> Out = TempOut
162 ; Out = [Identifier|TempOut]
163 ).
164
165 is_global(Identifier) :-
166 get_texpr_id(Identifier,Name), get_texpr_type(Identifier,Type),
167 Type = global(Set), is_b_global_constant(Set,_Nr,Name).
168 is_global(Identifier) :-
169 get_texpr_id(Identifier,Name), get_texpr_type(Identifier,Type),
170 Type = set(global(Name)), b_global_set(Name).
171 */
172
173 :- use_module(b_interpreter_components).
174 :- use_module('kodkod/kodkod',[apply_kodkod/3]).
175 :- use_module(specfile,[state_corresponds_to_set_up_constants/2]).
176 :- use_module(state_space,[visited_expression/2]).
177 :- use_module(library(lists),[maplist/2]).
178 solve_predicate_aux3(Pred,State,TimeoutFactor,Options,Res) :-
179 % find all identifiers used in the predicate and create a state
180 % was done by the caller, leading to code duplication
181 (member(full_machine_state,Options) -> get_full_machine_state_typed_identifiers(FilteredIdentifiers)
182 ; find_typed_identifier_uses(Pred,FilteredIdentifiers)),
183 !,
184 % global constants should not appear in the state
185 % otherwise they treated are as variables by the solver
186 % -> we now call find_typed_identifier_uses; we used to call filter_global_identifiers(Identifiers,FilteredIdentifiers),
187 % state may be predefined / partially set up
188 apply_kodkod(FilteredIdentifiers,Pred,Pred2),
189 (member(solve_in_visited_state(StateID),Options)
190 -> set_up_typed_localstate_for_pred(FilteredIdentifiers,Pred2,TypedVals,State),
191 (visited_expression(StateID,StateExpr),
192 state_corresponds_to_set_up_constants(StateExpr,FullStore)
193 -> maplist(initialise_localstate(FullStore),State)
194 ; StateID=root -> true
195 ; add_internal_error('Illegal State:',use_visited_state(StateID))
196 )
197 ; var(State)
198 -> set_up_typed_localstate_for_pred(FilteredIdentifiers,Pred2,TypedVals,State)
199 % TypedVals contains typedval(Val,Type,Var,TriggersEnumWarning)
200 % TO DO: add static_symmetry detection b_global_sets:static_symmetry_reduction_for_global_sets(State),
201 ; add_warning(solve_predicate,'Non-variable state: ',State)
202 % TypedVals is unbound, is this a problem !?? TO DO: investigate
203 ),
204 %tools_printing:print_term_summary(b_trace_test_components(Pred2,State,TypedVals)),nl,
205 b_interpreter_components:reset_component_info(false),
206 time_out_with_factor_call(
207 b_interpreter_components:b_trace_test_components(Pred2,State,TypedVals),
208 TimeoutFactor, [silent],
209 TO='time_out'),
210 (TO==time_out -> Res = TO ; Res = yes).
211
212 % set up a complete typed solver state for machine with all constants and variables, even if typing invariants removed and no reference anymore to variable in Invariant
213 % TypedVals need to be enumerated after calling solve_predicate, in case some variables are not constrained
214 get_full_machine_state_typed_identifiers(TIds) :-
215 b_get_machine_variables(Variables),
216 b_get_machine_constants(Constants), append(Constants,Variables,TIds).
217
218 :- use_module(static_ordering,[sort_ids_by_usage/4,reorder_state/3]).
219 set_up_typed_localstate_for_pred(CV,Pred,TypedVals,State) :-
220 (preferences:preference(use_static_ordering,true)
221 -> static_ordering:sort_ids_by_usage(CV,Pred,SortedVarlist,no_warnings) ,
222 set_up_typed_localstate(SortedVarlist,_FreshVars,TypedVals,[],SortedState,positive),
223 reorder_state(CV,SortedState,State) % SortedState can have issues with pack_state skeleton
224 ; set_up_typed_localstate(CV,_FreshVars,TypedVals,[],State,positive)).
225
226 % initialise local state with values found in bind list
227 initialise_localstate(FullStore,bind(Var,Val)) :-
228 (member(bind(Var,VarVal),FullStore)
229 -> Val=VarVal ; true).
230
231 % translate solver_result for use by Disprover/Prover or similar:
232 solver_result(time_out,_P,_State,_,time_out).
233 solver_result(error,_P,_State,_,error).
234 solver_result(no,P,_State,_,Res) :-
235 % there are two cases in which we might have a false negative:
236 % we enumerated a critical variable
237 %\+ event_occurred_in_error_scope(enumeration_warning(_,_,_,_,critical)), % we now check this in solve_predicate_aux1
238 % or there are unfixed / deferred global sets involved.
239 (predicate_uses_unfixed_deferred_set(P,CType)
240 % no unfixed global sets involved -> real contradiction found
241 -> debug_println(10,solver_predicate_contains_unfixed_deferred_set(CType)),
242 Res=no_solution_found(unfixed_deferred_sets)
243 ; Res=contradiction_found
244 ).
245 solver_result(virtual_time_out(Reason),_P,_State,_,no_solution_found(Reason)) :-
246 true. % this is now checked in solve_predicate_aux1: event_occurred_in_error_scope(enumeration_warning(A,B,C,D,critical)).
247 solver_result(overflow,_P,_State,_,no_solution_found(clpfd_overflow)).
248 solver_result(yes,_P,State,Options,solution(Solution)) :-
249 findall(binding(Id,EValue,PPValue),
250 (member(bind(Id,Value),State),
251 (solver_pp_bvalue(Value,Options,EValue,PPValue) -> true ; PPValue='**pretty-print failed**')),
252 Solution).
253
254 % expand and pretty print solver result values:
255 solver_pp_bvalue(Value,Options,EValue,PPValue) :- member(force_evaluation,Options),!,
256 EXPAND=force,
257 normalise_value_for_var(solver_result,EXPAND,Value,EValue),
258 solver_pp_bvalue2(EValue,Options,PPValue).
259 solver_pp_bvalue(Value,Options,Value,PPValue) :- solver_pp_bvalue2(Value,Options,PPValue).
260
261 solver_pp_bvalue2(Value,Options,PPValue) :- member(truncate(Limit),Options),!,
262 translate_bvalue_with_limit(Value,Limit,PPValue). % should we also control expand_avl_upto ??
263 solver_pp_bvalue2(Value,Options,PPValue) :- member(truncate,Options),!,
264 translate_bvalue_with_limit(Value,10000,PPValue).
265 solver_pp_bvalue2(Value,_,PPValue) :- translate_bvalue(Value,PPValue).
266
267 % check if a typed expression references an unfixed deferred set in some inner node
268 predicate_uses_unfixed_deferred_set(TExpr) :- predicate_uses_unfixed_deferred_set(TExpr,_).
269
270 predicate_uses_unfixed_deferred_set(TExpr,CType) :-
271 unfixed_deferred_set(_), % check first if some unfixed deferred set exists
272 map_over_typed_bexpr(solver_interface:uses_unfixed_ds_aux,TExpr,CType).
273 :- public uses_unfixed_ds_aux/2.
274 uses_unfixed_ds_aux(TExpr,Type) :- get_texpr_type(TExpr,Type),
275 contains_unfixed_deferred_set(Type).
276
277 % find a typed id whose type references an unfixed deferred set
278 unfixed_typed_id_in_list(TID,CType,Constants) :-
279 unfixed_deferred_set(_), % check first if some unfixed deferred set exists
280 member(TID,Constants), get_texpr_type(TID,CType),
281 contains_unfixed_deferred_set(CType).
282
283 :- use_module(library(lists),[maplist/3, include/3]).
284 type_check_in_machine_context(Predicates, TPredicates) :-
285 % create an empty type environment
286 % and store the known types of variables / constants
287 % otherwise, we might be unable to type variables from
288 % machines somewhere in the refinement hierarchy
289 % (their typing predicate might not be in the hypothesis)
290 % Definitions are also added to the environment since the typechecker
291 % inlines definitions.
292 env_empty(E),
293 b_get_machine_variables(V), b_get_machine_constants(C),
294 % add theory operators
295 full_b_machine(Machine),
296 get_section(operators,Machine,Operators),
297 keys_and_values(Operators,Ids,Ops),
298 foldl(env_store_operator,Ids,Ops,E,EWithTheories),
299 get_section(definitions, Machine, Definitions),
300 foldl(env_store_definition, Definitions, EWithTheories, EWithDefinitions),
301 % constants and variables
302 findall(b(identifier(Name),global(GlobalSet),[enumerated_set_element]),is_b_global_constant(GlobalSet,_Nr,Name),GC),
303 findall(b(identifier(Set),set(global(Set)),[given_set]),b_global_set(Set),GS),
304 add_identifiers_to_environment(V,EWithDefinitions,EV),
305 add_identifiers_to_environment(C,EV,EVC),
306 add_identifiers_to_environment(GC,EVC,EVCGC),
307 add_identifiers_to_environment(GS,EVCGC,EOut),
308 opentype(Predicates, EOut, Identifiers, DeferredSets, TPredicates),
309 % DeferredSets should be empty? I.e. should this be allowed to create new types?
310 % currently this would not work as these types have not been added/registered in b_global_sets ! kernel_objects:basic_type would fail
311 (DeferredSets = [] -> true
312 ; include(problematic_id(DeferredSets),Identifiers,TErrIds),
313 maplist(get_texpr_id,TErrIds,ErrIds),
314 add_warning(solver_interface,'The types of some variables could not be determined: ',ErrIds),
315 fail).
316 % a possible refactoring for the above - however, it does not type with open identifiers
317 % reverted it for now
318 % bmachine:b_type_expressions(Predicates,[constants],_Types,TPredicates,Errors),
319 % add_all_perrors(Errors,[],type_expression_error),
320 % no_real_perror_occurred(Errors).
321
322 problematic_id(DeferredSets,TID) :- get_texpr_type(TID,Type),
323 uses_def_set(Type,DeferredSets).
324
325 uses_def_set(global(T),DeferredSets) :- member(T,DeferredSets).
326 uses_def_set(set(A),DeferredSets) :- uses_def_set(A,DeferredSets).
327 uses_def_set(seq(A),DeferredSets) :- uses_def_set(A,DeferredSets).
328 uses_def_set(couple(A,_),DeferredSets) :- uses_def_set(A,DeferredSets).
329 uses_def_set(couple(_,A),DeferredSets) :- uses_def_set(A,DeferredSets).
330 uses_def_set(record(F),DS) :- member(field(_,T),F), uses_def_set(T,DS).
331 % TO DO: add freetypes