1 | % (c) 2019-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(dpllt_solver, [dpllt_solve_predicate/3, | |
6 | dpllt_solve_predicate/4, | |
7 | dpllt_solve_predicate_in_state/4, | |
8 | dpllt_solve_predicate_in_state/5, | |
9 | get_amount_of_sat_variables/2]). | |
10 | ||
11 | :- use_module(library(lists)). | |
12 | :- use_module(library(timeout)). | |
13 | :- use_module(library(sets), [subtract/3]). | |
14 | :- use_module(library(clpfd), [fd_var/1, fd_min/2, fd_max/2]). | |
15 | ||
16 | :- use_module(smt_solvers_interface(ast_optimizer_for_smt)). | |
17 | ||
18 | :- use_module(dpllt_solver('symmetry_check/smt_symmetry_breaking')). | |
19 | :- use_module(dpllt_solver('dpllt_preprocessing')). | |
20 | :- use_module(dpllt_solver('dpllt_pred_to_sat')). | |
21 | :- use_module(dpllt_solver('dpllt_sat_solver')). | |
22 | :- use_module(dpllt_solver('difference_logic/difference_logic_solver')). | |
23 | :- use_module(dpllt_solver('difference_logic/ast_to_difference_logic')). | |
24 | ||
25 | :- use_module(probsrc(debug)). | |
26 | :- use_module(probsrc(b_ast_cleanup), [clean_up_pred/3]). | |
27 | :- use_module(probsrc(preferences), [get_preference/2, | |
28 | temporary_set_preference/2, | |
29 | reset_temporary_preference/1]). | |
30 | :- use_module(probsrc(b_compiler), [b_compile/6]). | |
31 | :- use_module(probsrc(b_enumerate), [b_tighter_enumerate_all_values/2]). | |
32 | :- use_module(probsrc(tools_meta),[safe_time_out/3]). | |
33 | :- use_module(probsrc(translate), [print_bexpr/1]). | |
34 | :- use_module(probsrc(tools), [start_ms_timer/1,stop_ms_timer_with_debug_msg/2]). | |
35 | :- use_module(probsrc(kernel_waitflags), [init_wait_flags/2,ground_wait_flags/1]). | |
36 | :- use_module(probsrc(kernel_objects), [infer_value_type/2]). | |
37 | :- use_module(probsrc(b_interpreter), [set_up_typed_localstate/6, | |
38 | b_test_boolean_expression/4, | |
39 | b_convert_bool/5]). | |
40 | :- use_module(probsrc(error_manager), [add_error/3, | |
41 | add_internal_error/2, | |
42 | add_message/2, | |
43 | check_error_occured/2, | |
44 | enter_new_error_scope/2, | |
45 | exit_error_scope/3, | |
46 | clear_enumeration_warnings/0, | |
47 | clear_wd_errors/0, | |
48 | critical_enumeration_warning_occured_in_error_scope/4]). | |
49 | :- use_module(probsrc(bsyntaxtree), [find_typed_identifier_uses/2, | |
50 | find_identifier_uses/3, | |
51 | check_ast/1, | |
52 | get_texpr_info/2, | |
53 | conjunct_predicates/2]). | |
54 | :- use_module(probsrc(module_information), [module_info/2]). | |
55 | ||
56 | :- module_info(group, dpllt). | |
57 | :- module_info(description,'This module provides a DPLL(T) based solver for B.'). | |
58 | ||
59 | :- dynamic initial_solution/0. | |
60 | :- volatile initial_solution/0. | |
61 | ||
62 | debug_format_dpllt(_, _) :- | |
63 | debug_mode(off), | |
64 | !. | |
65 | debug_format_dpllt(Msg, Vars) :- | |
66 | append(Msg, " (DPLL(T) Solver)~n", NCodes), | |
67 | atom_codes(NMsg, NCodes), | |
68 | format(NMsg, Vars), !. | |
69 | ||
70 | debug_format_dpllt(_, _, _) :- | |
71 | debug_mode(off), | |
72 | !. | |
73 | debug_format_dpllt(Msg, Vars, Pred) :- | |
74 | format(Msg, Vars), | |
75 | translate:print_bexpr(Pred), nl, !. | |
76 | ||
77 | init :- | |
78 | %retract(debug:debug_mode(_)), | |
79 | %asserta(debug:debug_mode(on)), | |
80 | % enumeration has to be linear for symmetry breaking but also set to false by default | |
81 | temporary_set_preference(randomise_enumeration_order, false), | |
82 | dpllt_pred_to_sat:reset_sat_var_id, | |
83 | retractall(initial_solution), | |
84 | retractall(idl_candidate_constants(_,_)), | |
85 | %temporary_set_preference(use_chr_solver, true), | |
86 | temporary_set_preference(unsat_core_algorithm, divide_and_conquer), % default is linear_greedy | |
87 | temporary_set_preference(use_smt_mode, false), | |
88 | temporary_set_preference(use_clpfd_solver, true), | |
89 | temporary_set_preference(optimize_ast, true), | |
90 | temporary_set_preference(use_common_subexpression_elimination, false), | |
91 | temporary_set_preference(normalize_ast_sort_commutative, false), | |
92 | temporary_set_preference(normalize_ast, false). | |
93 | ||
94 | reset_preferences :- | |
95 | reset_temporary_preference(randomise_enumeration_order), | |
96 | reset_temporary_preference(unsat_core_algorithm), | |
97 | reset_temporary_preference(use_smt_mode), | |
98 | reset_temporary_preference(use_clpfd_solver), | |
99 | reset_temporary_preference(optimize_ast), | |
100 | reset_temporary_preference(use_common_subexpression_elimination), | |
101 | reset_temporary_preference(normalize_ast_sort_commutative), | |
102 | reset_temporary_preference(normalize_ast). | |
103 | ||
104 | set_current_state(Pred, State, NPred) :- | |
105 | reset_optimizer_state, | |
106 | assert_state_id_values(State), | |
107 | replace_ids_with_ground_values(Pred, 0, [], NPred), | |
108 | reset_optimizer_state, !. | |
109 | ||
110 | %% dpllt_solve_predicate(+Pred, -SolvedPred, -Result). | |
111 | % Main interface predicate for DPLL(T) based solver which times out | |
112 | % with respect to ProB's time_out preference. | |
113 | dpllt_solve_predicate(Pred, SolvedPred, Result) :- | |
114 | dpllt_solve_predicate(default, Pred, SolvedPred, Result). | |
115 | ||
116 | %% dpllt_solve_predicate(+SolverName, +Pred, -SolvedPred, -Result). | |
117 | dpllt_solve_predicate(SolverName, Pred, SolvedPred, Result) :- | |
118 | init, | |
119 | get_preference(time_out, Timeout), | |
120 | safe_time_out(dpllt_solve_predicate_initialized(SolverName, Pred, TSolvedPred, SolverResult), | |
121 | Timeout, | |
122 | TimeoutResult), | |
123 | ( TimeoutResult == time_out | |
124 | -> Result = time_out, | |
125 | SolvedPred = Pred | |
126 | ; ground(SolverResult), | |
127 | Result = SolverResult, | |
128 | SolvedPred = TSolvedPred | |
129 | ). | |
130 | ||
131 | %% dpllt_solve_predicate_in_state(+Pred, +Pred, -Result). | |
132 | dpllt_solve_predicate_in_state(Pred, State, SolvedPred, Result) :- | |
133 | dpllt_solve_predicate_in_state(default, Pred, State, SolvedPred, Result). | |
134 | ||
135 | %% dpllt_solve_predicate_in_state(+SolverName, +Pred, +Pred, -Result). | |
136 | dpllt_solve_predicate_in_state(SolverName, Pred, State, SolvedPred, Result) :- | |
137 | set_current_state(Pred, State, NPred), | |
138 | get_equalities_from_bindings(State, EqConj), | |
139 | dpllt_solve_predicate(SolverName, b(conjunct(EqConj,NPred),pred,[]), SolvedPred, Result). | |
140 | ||
141 | dpllt_solve_predicate_initialized(SolverName, Pred, SolvedPred, Result) :- | |
142 | ( check_ast(Pred) | |
143 | -> ( check_error_occured(check_ast_typing, _) | |
144 | -> % input AST is not correctly typed | |
145 | SolvedPred = Pred, | |
146 | Result = error | |
147 | ; dpllt_solve_predicate_initialized_checked(SolverName, Pred, SolvedPred, Result) | |
148 | ) | |
149 | ; add_message(dpllt_solve_predicate_no_timeout, 'Input predicate is not well-defined~nProB\'s SMT solver transforms the input to a well-defined predicate before constraint solving~n'), | |
150 | dpllt_solve_predicate_initialized_checked(SolverName, Pred, SolvedPred, Result) | |
151 | ). | |
152 | ||
153 | dpllt_solve_predicate_initialized_checked(SolverName, Pred, SolvedPred, Result) :- | |
154 | call_cleanup(catch(dpllt_solve_predicate_no_timeout(SolverName, Pred, SolvedPred, Result), | |
155 | Exception, % TODO: catch specific exception instead of all | |
156 | handle_clpfd_overflow(SolverName, Exception, Pred, SolvedPred, Result) | |
157 | ), | |
158 | reset_preferences | |
159 | ). | |
160 | ||
161 | get_equalities_from_bindings([], b(truth,pred,[])). | |
162 | get_equalities_from_bindings([bind(Id,Val)|T], EqConj) :- | |
163 | get_equality_from_binding(bind(Id,Val), Eq), | |
164 | get_equalities_from_bindings(T, Eq, EqConj). | |
165 | ||
166 | get_equalities_from_bindings([], EqConj, EqConj). | |
167 | get_equalities_from_bindings([bind(Id,Val)|T], EqAcc, EqConj) :- | |
168 | get_equality_from_binding(bind(Id,Val), Eq), | |
169 | get_equalities_from_bindings(T, b(conjunct(Eq,EqAcc),pred,[]), EqConj). | |
170 | ||
171 | %% handle_clpfd_overflow(+SolverName, +Exception, +Pred, -SolvedPred, -Result). | |
172 | % Disable CLP(FD) interface and restart DPLL(T) if an overflow occured. | |
173 | handle_clpfd_overflow(SolverName, Exception, Pred, SolvedPred, Result) :- | |
174 | Exception = error(representation_error(Err),_), | |
175 | memberchk(Err, ['CLPFD integer overflow','max_clpfd_integer','min_clpfd_integer']), | |
176 | !, | |
177 | temporary_set_preference(use_clpfd_solver, false), | |
178 | dpllt_solve_predicate_initialized(SolverName, Pred, SolvedPred, Result). | |
179 | handle_clpfd_overflow(_, Exception, _, _, unknown) :- | |
180 | add_error(dpllt,'Exception occurred during solving:',Exception). | |
181 | ||
182 | % only for computing the amount of sat variables of benchmarks | |
183 | get_amount_of_sat_variables(Pred, AmountOfSatVars) :- | |
184 | simplify_negation(Pred, SPred), | |
185 | reset_optimizer_state, | |
186 | assert_ground_id_values(0, SPred), | |
187 | replace_ids_with_ground_values(SPred, 0, [], AstCardOpt), | |
188 | precompute_values(AstCardOpt, [instantiate_quantifier_limit(2)], AstPrecomputed), | |
189 | %writeq(AstCompiled),nl, | |
190 | (clean_up_pred(AstPrecomputed, _, CleanPred) -> true | |
191 | ; add_internal_error('Clean up failed ', dpllt), CleanPred=AstPrecomputed), | |
192 | ( CleanPred = b(truth,pred,_) | |
193 | -> AmountOfSatVars = 1 | |
194 | ; CleanPred = b(falsity,pred,_) | |
195 | -> AmountOfSatVars = 1 | |
196 | ; preprocess_predicate(false, false, CleanPred, LiftedPred, _, _), | |
197 | predicate_to_sat(normal, LiftedPred, _, _, _, SatVars1) | |
198 | ), | |
199 | length(SatVars1, AmountOfSatVars). | |
200 | ||
201 | %% dpllt_solve_predicate(+SolverName, +Pred, -SolvedPred, -SolverResult). | |
202 | % Interface predicate for DPLL(T) based solver without a timeout. | |
203 | dpllt_solve_predicate_no_timeout(SolverName, Pred, SolvedPred, SolverResult) :- | |
204 | %nl, write('Initial pred: '), nl, translate:print_bexpr(Pred), nl, | |
205 | %nl, write('Initial pred: '),nl,writeq(Pred),nl, | |
206 | %unfold_let(Pred, Unfolded), | |
207 | %nl, write('Unfolded pred: '), nl, translate:print_bexpr(Unfolded), nl, | |
208 | %( preferences:get_preference(smtlib2b_cleanup_predicate, true) | |
209 | %-> | |
210 | debug_format_dpllt("After parsing: ", [], Pred), | |
211 | debug_format_dpllt("~nStart preprocessing", []), | |
212 | start_ms_timer(T0), | |
213 | simplify_negation(Pred, SPred), | |
214 | debug_format_dpllt("After simplifying negations: ", [], SPred), | |
215 | reset_optimizer_state, | |
216 | assert_ground_id_values(0, SPred), % assert for later incremental clause learning | |
217 | %replace_ids_with_ground_values(SPred, 0, [], AstOpt), % this is slower for DPLL(T) | |
218 | precompute_values(SPred, [instantiate_quantifier_limit(2)], AstPrecomputed), | |
219 | debug_format_dpllt("After precomputing values: ", [], SPred), | |
220 | stop_ms_timer_with_debug_msg(T0,precompute_values_for_dpllt), | |
221 | find_identifier_uses(AstPrecomputed, [], UsedIds), | |
222 | temporary_set_preference(data_validation_mode, true), | |
223 | b_compile(AstPrecomputed, UsedIds, [], [], AstCompiled, no_wf_available), | |
224 | clear_wd_errors, % b_compile might throw a wd error | |
225 | reset_temporary_preference(data_validation_mode), | |
226 | debug_format_dpllt("After compiling values: ", [], AstCompiled), | |
227 | start_ms_timer(T1), | |
228 | (clean_up_pred(AstCompiled, _, CleanPred) -> true | |
229 | ; add_internal_error('Clean up failed ',SolverName), CleanPred=AstCompiled), | |
230 | stop_ms_timer_with_debug_msg(T1,clean_up_pred_for_dpllt), | |
231 | start_ms_timer(T2), | |
232 | debug_format_dpllt("After AST cleanup: ", [], CleanPred), | |
233 | %nl, write('Clean + sym pred: '), nl, translate:print_bexpr(SymPred), nl, | |
234 | %; CleanPred = Pred | |
235 | %), | |
236 | %nl, write('Clean pred: '),nl,writeq(CleanPred),nl, | |
237 | %nl, write('Sym pred: '),nl,translate:print_bexpr(SymPred),nl, | |
238 | ( CleanPred = b(truth,pred,_) | |
239 | -> SolverResult = solution([]), | |
240 | SolvedPred = b(truth,pred,[]) | |
241 | ; CleanPred = b(falsity,pred,_) | |
242 | -> SolverResult = contradiction_found, | |
243 | SolvedPred = b(falsity,pred,[]) | |
244 | ; is_rewrite_to_idl(RewriteToIdl), | |
245 | ( dpllt_settings:static_smt_symmetry_breaking(true) | |
246 | -> debug_format_dpllt("Start symmetry breaking..", []), | |
247 | ( get_symmetry_breaking_predicates_decomposed(CleanPred, SymPred) | |
248 | ; add_message(smt_symmetry_breaking, 'Symmetry breaking failed on the top-level.'), | |
249 | SymPred = b(truth,pred,[]) | |
250 | ) | |
251 | ; SymPred = b(truth,pred,[]) | |
252 | ), | |
253 | debug_format_dpllt("done.", []), | |
254 | dpllt_settings:static_syntax_analysis(PerformStaticAnalysis), | |
255 | preprocess_predicate(PerformStaticAnalysis, RewriteToIdl, CleanPred, LiftedPred, FilteredCandidateImplsConj, CandidateImpls), | |
256 | debug_format_dpllt("Lifted predicate: ", [], LiftedPred), | |
257 | predicate_to_sat(normal, LiftedPred, Env1, WDPOs1, SmtBoolFormula, SatVars1), | |
258 | preprocess_predicate(false, RewriteToIdl, SymPred, LiftedSymPred, _, _), | |
259 | predicate_to_sat(normal, WDPOs1, SatVars1, Env1, LiftedSymPred, Env2, WDPOs2, SymBoolFormula, SatVars2), | |
260 | ( PerformStaticAnalysis=true | |
261 | -> predicate_to_sat(only_reuse, WDPOs2, SatVars2, Env2, FilteredCandidateImplsConj, Env3, WDPOsList, ImplBoolFormula, SatVars3), | |
262 | debug_format_dpllt("Static analysis inferred: ", [], ImplBoolFormula) | |
263 | ; ImplBoolFormula = b(truth,pred,[]), | |
264 | SatVars3 = SatVars2, Env3 = Env2, WDPOsList = WDPOs2 | |
265 | ), | |
266 | % well-definedness implication on the top-level to encode well-definedness in the SAT solver | |
267 | dpllt_sat_solver:init_dynamic_solver_state(SolverName), % TODO: check if this can be moved inside the SAT solver again | |
268 | conjunct_predicates(WDPOsList, WDPOs), | |
269 | preprocess_predicate(false, RewriteToIdl, WDPOs, _, _, WDCandidateImpls), | |
270 | % theory deduction for wd | |
271 | get_wd_theory_implications(CandidateImpls, WDCandidateImpls, WDTheoryImpls), | |
272 | predicate_to_sat(normal, WDTheoryImpls, SatVars3, Env3, WDPOs, Env4, _, WDTheoryImplsBoolFormula, SatVars4), | |
273 | debug_format_dpllt("WD Theory Deduction Implications: ", [], WDTheoryImpls), | |
274 | predicate_to_sat(normal, WDPOs, SatVars4, Env4, WDPOs, NewEnv, _, WDPosBoolFormula, SatVars), | |
275 | debug_format_dpllt("WD Implication: ", [], WDPOs), | |
276 | debug_format_dpllt("WD Implication as Boolean formula: ", [], WDPosBoolFormula), | |
277 | !, | |
278 | conjunct_predicates([SymBoolFormula,WDTheoryImplsBoolFormula,ImplBoolFormula], AnalysisBoolFormula), | |
279 | find_typed_identifier_uses(LiftedPred, SmtVars), % TO DO: improve | |
280 | debug_format_dpllt("Check LiftedPred", []), | |
281 | ( check_ast(LiftedPred) | |
282 | -> true | |
283 | ; add_internal_error('AST is missing well-definedness information for solver ', SolverName) | |
284 | ), | |
285 | dpllt_sat_solver:cnf_to_smt(SmtBoolFormula, SolvedPred), | |
286 | stop_ms_timer_with_debug_msg(T2,preprocessing_for_dpllt), | |
287 | debug_format_dpllt("End preprocessing", []), | |
288 | start_ms_timer(T3), | |
289 | enter_new_error_scope(ScopeID, dpllt_solve_predicate), | |
290 | %nl, write('smtBoolFormula: '), nl, translate:print_bexpr(SmtBoolFormula), nl, | |
291 | %nl, write('Analysis Bool formula: '), nl, translate:print_bexpr(AnalysisBoolFormula), nl, | |
292 | call_cleanup(conflict_driven_clause_learning_online(SolverName, NewEnv, SmtVars, SatVars, WDPosBoolFormula, AnalysisBoolFormula, SmtBoolFormula, SolverResult), | |
293 | exit_error_scope(ScopeID, _, dpllt_solve_predicate)), | |
294 | stop_ms_timer_with_debug_msg(T3,dpllt_solving_success) | |
295 | ). | |
296 | dpllt_solve_predicate_no_timeout(_, Pred, _, _) :- | |
297 | add_error(dpllt_solve_predicate, 'Cannot create SAT formula from B predicate:', [Pred]). | |
298 | ||
299 | is_rewrite_to_idl(Res) :- | |
300 | get_preference(dpllt_use_idl_theory_solver,true), | |
301 | !, | |
302 | Res = true. | |
303 | is_rewrite_to_idl(false). | |
304 | ||
305 | %% conflict_driven_clause_learning_online(+SolverName, +Env, +SmtVars, +SatVars, +WDPosBoolFormula, +AnalysisBoolFormula, +BoolFormula, -SolverResult). | |
306 | % Conflict-driven clause learning from incomplete assignments of the boolean formula by setting up reification constraints | |
307 | % connecting the SAT and theory solvers. | |
308 | conflict_driven_clause_learning_online(_, _, _, _, _, _, b(truth,pred,_), SolverResult) :- | |
309 | !, | |
310 | SolverResult = solution([]). | |
311 | conflict_driven_clause_learning_online(_, _, _, _, _, _, b(falsity,pred,_), SolverResult) :- | |
312 | !, | |
313 | SolverResult = contradiction_found. | |
314 | conflict_driven_clause_learning_online(SolverName, Env, SmtVars, SatVars, WDPosBoolFormula, AnalysisBoolFormula, BoolFormula, SolverResult) :- | |
315 | optimize_clause_size_by_rewriting(BoolFormula, SatVars, TOptBoolFormula, NewSatVars, NewVarConjList), | |
316 | conjunct_predicates([AnalysisBoolFormula,TOptBoolFormula], TFullBoolFormula), | |
317 | conjunct_predicates(NewVarConjList, NewVarConj), | |
318 | ( NewVarConj = b(truth,pred,_) | |
319 | -> FullBoolFormula = TFullBoolFormula | |
320 | ; FullBoolFormula = b(conjunct(NewVarConj,TFullBoolFormula),pred,[]) | |
321 | ), | |
322 | %bool_formula_to_smt(FullBoolFormula, SmtFullBoolFormula), | |
323 | %translate:print_bexpr(FullBoolFormula), nl, | |
324 | get_bindings_from_ids(NewSatVars, SatBindings, SatVarNames, IdPrologVarTuples, PrologSatVarTriple), | |
325 | ( b_to_cnf(FullBoolFormula, SatBindings, CnfBoolFormula) | |
326 | -> true | |
327 | ; add_internal_error('B to CNF conversion failed ', dpllt) | |
328 | ), | |
329 | setup_theory_wf_store(SmtVars, SmtBindings, WfStoreSmt), | |
330 | ( get_preference(dpllt_use_idl_theory_solver,true) | |
331 | -> difference_logic_solver:init_idl_solver(IdlGraphMut) | |
332 | ; true | |
333 | ), | |
334 | ( WDPosBoolFormula \= b(truth,pred,_) | |
335 | -> % add WD PO implications | |
336 | b_to_cnf(WDPosBoolFormula, SatBindings, CnfWDPosBoolFormula), | |
337 | append(CnfWDPosBoolFormula, CnfBoolFormula, TNCnfBoolFormula) | |
338 | ; TNCnfBoolFormula = CnfBoolFormula | |
339 | ), | |
340 | dpllt_sat_solver:create_solver_state(SatStateMutable), | |
341 | SolverEnv = [sat_vars:NewSatVars,sat_varnames:SatVarNames,sat_bindings:SatBindings,t2b_env:Env,pl_vars:PrologSatVarTriple,smt_bindings:SmtBindings], | |
342 | dpllt_sat_solver:unit_propagate_cnf(SolverEnv, SatStateMutable, TNCnfBoolFormula, TNCnfBoolFormula2), | |
343 | sort(TNCnfBoolFormula2, NCnfBoolFormula), | |
344 | ( debug_mode(off) | |
345 | -> true | |
346 | ; dpllt_sat_solver:portray_cnf(NCnfBoolFormula) | |
347 | ), | |
348 | setup_reification(SatStateMutable, IdlGraphMut, IdPrologVarTuples, SmtBindings, WfStoreSmt), | |
349 | %format("Initial Smt Bindings after deterministic unit propagation:~n~w~n", [SmtBindings]), | |
350 | dpllt_sat_solver:sat(SolverName, SolverEnv, SatStateMutable, NCnfBoolFormula), | |
351 | %nl,nl,format("Exit sat solver, ground waitflags~n", []), | |
352 | %dpllt_sat_solver:portray_sat_stack(SatStateMutable), | |
353 | %\+ error_manager:check_error_occured(_, _), | |
354 | debug_format_dpllt("Ground waitflags", []), | |
355 | ( get_preference(dpllt_use_idl_theory_solver,true) | |
356 | -> propagate_fd_bounds_to_idl_solver(IdlGraphMut, SmtBindings),% TO DO: propagate from ProB to IDL solver earlier? | |
357 | get_idl_solution_bindings(IdlGraphMut, IdlBindings), | |
358 | ( % try one idl solution first since ProB could timeout, but do not enumerate idl solutions here | |
359 | set_bindings(IdlBindings, SmtBindings), | |
360 | ground_wait_flags(WfStoreSmt) | |
361 | ; exclude_idl_solution(IdlBindings, SmtBindings, WfStoreSmt), | |
362 | ( ground_wait_flags(WfStoreSmt) | |
363 | ; % fallback to idl solver and possibly enumerate all solutions | |
364 | critical_enumeration_warning_occured_in_error_scope(_, _, _, _), | |
365 | clear_enumeration_warnings, | |
366 | propagate_idl_solution_to_bindings_bt(IdlGraphMut, SmtBindings), | |
367 | ground_wait_flags(WfStoreSmt) | |
368 | ) | |
369 | ) | |
370 | ; ground_wait_flags(WfStoreSmt) | |
371 | ), | |
372 | ( check_error_occured(ErrorName, _), | |
373 | ErrorName \== warning(smt_symmetry_breaking), | |
374 | ErrorName \== smt_symmetry_breaking, | |
375 | fail | |
376 | ; true | |
377 | ), | |
378 | SolverResult = solution(SmtBindings), | |
379 | log_solution, | |
380 | announce_bt_from_smt_solution. | |
381 | conflict_driven_clause_learning_online(_, _, _, _, _, _, _, Res) :- | |
382 | \+ check_error_occured(well_definedness_error, _), | |
383 | critical_enumeration_warning_occured_in_error_scope(A, B, C, D), | |
384 | !, | |
385 | clear_enumeration_warnings, | |
386 | Res = no_solution_found(enumeration_warning(A,B,C,D,critical)). | |
387 | conflict_driven_clause_learning_online(_, _, _, _, _, _, _, Res) :- | |
388 | check_error_occured(ErrorName, _), | |
389 | ErrorName \== smt_symmetry_breaking, | |
390 | ErrorName \== warning(smt_symmetry_breaking), | |
391 | ErrorName \== well_definedness_error, | |
392 | ErrorName \== internal_error(b_compiler), | |
393 | !, | |
394 | Res = error. | |
395 | conflict_driven_clause_learning_online(_, _, _, _, _, _, _, error) :- | |
396 | \+ initial_solution, | |
397 | check_error_occured(well_definedness_error, Reason), | |
398 | add_error(well_definedness_error, Reason, 'conflict_driven_clause_learning_online'). | |
399 | conflict_driven_clause_learning_online(_, _, _, _, _, _, _, contradiction_found) :- | |
400 | \+ initial_solution, | |
401 | \+ check_error_occured(well_definedness_error, _). | |
402 | ||
403 | %% setup_reification(+SatStateMutable, +IdlGraphMut, +IdPrologVarTuples, +SmtBindings, +WfStoreSmt). | |
404 | % Set up reification constraints to connect SAT and SMT solver. | |
405 | % Propagates in both directions (b_convert_bool/5 implements theory propagation). | |
406 | setup_reification(_, _, [], _, _). | |
407 | setup_reification(SatStateMutable, IdlGraphMut, [(SatId,SatPrologVar)|T], SmtBindings, WfStoreSmt) :- | |
408 | get_texpr_info(SatId, Info), | |
409 | memberchk(smt_formula(SmtFormula), Info), | |
410 | !, | |
411 | SatId = b(identifier(SatVarName),_,_), | |
412 | setup_reification_for_solver(SatStateMutable, IdlGraphMut, SmtFormula, SmtBindings, WfStoreSmt, SatVarName, SatPrologVar), | |
413 | %sat_smt_reification(SatId, SatPrologVar, SmtBindings, WfStoreSmt), | |
414 | setup_reification(SatStateMutable, IdlGraphMut, T, SmtBindings, WfStoreSmt). | |
415 | setup_reification(SatStateMutable, IdlGraphMut, [_|T], SmtBindings, WfStoreSmt) :- | |
416 | % plain SAT variable which is not reified with a theory solver, e.g., introduced by CNF optimization rewriting | |
417 | setup_reification(SatStateMutable, IdlGraphMut, T, SmtBindings, WfStoreSmt). | |
418 | ||
419 | /*:- block sat_smt_reification(?, -, ?, ?). | |
420 | % without theory propagation | |
421 | sat_smt_reification(SatId, SatPrologVar, SmtBindings, WfStoreSmt) :- | |
422 | get_texpr_info(SatId, Info), | |
423 | memberchk(smt_formula(TSmtFormula), Info), | |
424 | ( SatPrologVar == pred_true | |
425 | -> SmtFormula = TSmtFormula | |
426 | ; SmtFormula = b(negation(TSmtFormula),pred,[]) | |
427 | ), | |
428 | b_test_boolean_expression(SmtFormula, SmtBindings, [], WfStoreSmt).*/ | |
429 | ||
430 | setup_reification_for_solver(SatStateMutable, IdlGraphMut, SmtFormula, SmtBindings, WfStoreSmt, SatVarName, SatPrologVar) :- | |
431 | log_theory_propagation(SatStateMutable, SatVarName, SatPrologVar), | |
432 | ( get_preference(dpllt_use_idl_theory_solver,true) | |
433 | % TO DO: Problem beide Solver aufzusetzen: was ist wenn idl solver lösung findet aber prob timeout? | |
434 | -> ( ast_to_difference_logic:rewrite_to_idl(SmtFormula, [DLConstraint]) | |
435 | -> debug_format_dpllt("Constraint for IDL: ", [], SmtFormula), | |
436 | idl_solver_interface(IdlGraphMut, SatPrologVar, [DLConstraint]) | |
437 | ; log_idl_candidates_from_constraint(SmtFormula), | |
438 | propagate_non_idl_to_idl(IdlGraphMut, SatPrologVar, SmtFormula) | |
439 | ), | |
440 | sat_debug_msg(SatVarName,SmtFormula,SatPrologVar), | |
441 | b_convert_bool(SmtFormula, SmtBindings, [], WfStoreSmt, SatPrologVar) | |
442 | ; sat_debug_msg(SatVarName,SmtFormula,SatPrologVar), | |
443 | b_convert_bool(SmtFormula, SmtBindings, [], WfStoreSmt, SatPrologVar) | |
444 | ). | |
445 | ||
446 | sat_debug_msg(SatVarName,SmtFormula,SatPrologVar) :- | |
447 | (debug_mode(off) -> true | |
448 | ; format('% SAT variable ~w (~w) for ProB pred: ',[SatVarName,SatPrologVar]), | |
449 | print_bexpr(SmtFormula),nl). | |
450 | ||
451 | :- block log_theory_propagation(?, ?, -). | |
452 | log_theory_propagation(SatStateMutable, SatVarName, SatPrologVar) :- | |
453 | ground(SatPrologVar), | |
454 | ( is_last_propagated_sat_var(SatStateMutable, SatVarName) | |
455 | -> true | |
456 | ; dpllt_sat_solver:log_theory_propagation_sat_stack(SatStateMutable, SatVarName, SatPrologVar) | |
457 | ). | |
458 | ||
459 | :- dynamic idl_candidate_constants/2. | |
460 | :- volatile idl_candidate_constants/2. | |
461 | ||
462 | log_idl_candidates_from_constraint(Constraint) :- | |
463 | get_ids_and_int_constants(Constraint, Ids, IntConstants), | |
464 | Ids \== [], | |
465 | IntConstants \== [], | |
466 | !, | |
467 | assert_idl_candidates(Ids, IntConstants). | |
468 | log_idl_candidates_from_constraint(_). | |
469 | ||
470 | assert_idl_candidates([], _). | |
471 | assert_idl_candidates([Id|T], IntConstants) :- | |
472 | retract(idl_candidate_constants(Id, Candidates)), | |
473 | !, | |
474 | subtract(IntConstants, Candidates, New), | |
475 | append(Candidates, New, NewCandidates), | |
476 | asserta(idl_candidate_constants(Id, NewCandidates)), | |
477 | assert_idl_candidates(T, IntConstants). | |
478 | assert_idl_candidates([Id|T], IntConstants) :- | |
479 | asserta(idl_candidate_constants(Id, IntConstants)), | |
480 | assert_idl_candidates(T, IntConstants). | |
481 | ||
482 | get_ids_and_int_constants(b(Node,pred,_), Ids, IntConstants) :- | |
483 | comparison_op(Node, Lhs, Rhs), | |
484 | get_ids_and_int_constants_expr(Lhs, [], [], IdsAcc, IntConstantsAcc), | |
485 | get_ids_and_int_constants_expr(Rhs, IdsAcc, IntConstantsAcc, Ids, IntConstants). | |
486 | ||
487 | get_ids_and_int_constants_expr(Id, IdsAcc, IntConstantsAcc, [Name|IdsAcc], IntConstantsAcc) :- | |
488 | Id = b(identifier(Name),integer,_). | |
489 | get_ids_and_int_constants_expr(Int, IdsAcc, IntConstantsAcc, IdsAcc, [Value,Value1|IntConstantsAcc]) :- | |
490 | ( Int = b(integer(Value),integer,_) | |
491 | ; Int = b(unary_minus(b(integer(Value),integer,_)),integer,_) | |
492 | ), | |
493 | % select positive and negative value | |
494 | Value1 is Value * -1. | |
495 | get_ids_and_int_constants_expr(b(Node,integer,_), IdsAcc, IntConstantsAcc, Ids, IntConstants) :- | |
496 | arithmetic_expr(Node, Lhs, Rhs), | |
497 | get_ids_and_int_constants_expr(Lhs, IdsAcc, IntConstantsAcc, NIdsAcc, NIntConstantsAcc), | |
498 | get_ids_and_int_constants_expr(Rhs, NIdsAcc, NIntConstantsAcc, Ids, IntConstants). | |
499 | ||
500 | comparison_op(less(Lhs,Rhs), Lhs, Rhs). | |
501 | comparison_op(less_equal(Lhs,Rhs), Lhs, Rhs). | |
502 | comparison_op(equal(Lhs,Rhs), Lhs, Rhs). | |
503 | comparison_op(not_equal(Lhs,Rhs), Lhs, Rhs). | |
504 | ||
505 | arithmetic_expr(minus(Lhs,Rhs), Lhs, Rhs). | |
506 | arithmetic_expr(add(Lhs,Rhs), Lhs, Rhs). | |
507 | arithmetic_expr(div(Lhs,Rhs), Lhs, Rhs). | |
508 | arithmetic_expr(floored_div(Lhs,Rhs), Lhs, Rhs). | |
509 | arithmetic_expr(power_of(Lhs,Rhs), Lhs, Rhs). | |
510 | arithmetic_expr(multiplication(Lhs,Rhs), Lhs, Rhs). | |
511 | arithmetic_expr(modulo(Lhs,Rhs), Lhs, Rhs). | |
512 | ||
513 | :- block propagate_non_idl_to_idl(?, -, ?). | |
514 | propagate_non_idl_to_idl(IdlGraphMut, SatPrologVar, SmtFormula) :- | |
515 | ( SatPrologVar == pred_true % TO DO: pred_false? | |
516 | % e.g., x:NAT -> x>=0 | |
517 | -> ( infer_constraints_for_idl_solver(SmtFormula, ConjList) | |
518 | -> register_constraints(IdlGraphMut, ConjList) | |
519 | ; true | |
520 | ) | |
521 | ; true | |
522 | ). | |
523 | ||
524 | %% idl_solver_interface(+IdlGraphMut, +SatPrologVar, +Constraint). | |
525 | :- block idl_solver_interface(?, -, ?). | |
526 | idl_solver_interface(IdlGraphMut, SatPrologVar, [DLConstraint]) :- | |
527 | SatPrologVar == pred_true, | |
528 | difference_logic_solver:remove_unsat_core, | |
529 | dpllt_sat_solver:remove_idl_unsat_core, | |
530 | ( difference_logic_solver:register_constraint(IdlGraphMut, DLConstraint) | |
531 | ; \+ dpllt_sat_solver:is_backjumping, | |
532 | propagate_idl_unsat_core_to_sat_solver(IdlGraphMut), | |
533 | fail | |
534 | ). | |
535 | idl_solver_interface(IdlGraphMut, SatPrologVar, [DLConstraint]) :- | |
536 | SatPrologVar == pred_false, | |
537 | difference_logic_solver:remove_unsat_core, | |
538 | dpllt_sat_solver:remove_idl_unsat_core, | |
539 | ( difference_logic_solver:register_constraint(IdlGraphMut, b(negation(DLConstraint),pred,[])) | |
540 | ; \+ dpllt_sat_solver:is_backjumping, | |
541 | propagate_idl_unsat_core_to_sat_solver(IdlGraphMut), | |
542 | fail | |
543 | ). | |
544 | ||
545 | propagate_idl_unsat_core_to_sat_solver(IdlGraphMut) :- | |
546 | difference_logic_solver:get_solver_result(IdlGraphMut, Res), | |
547 | Res == contradiction_found, | |
548 | difference_logic_solver:get_unsat_core(IdlCore), | |
549 | difference_logic_solver:remove_unsat_core, | |
550 | dpllt_sat_solver:store_idl_unsat_core(IdlCore). | |
551 | ||
552 | %% get_idl_solution_bindings(+IdlGraphMut, -Bindings). | |
553 | get_idl_solution_bindings(IdlGraphMut, Bindings) :- | |
554 | get_solver_result(IdlGraphMut, SolverResult), | |
555 | SolverResult = solution(Bindings). | |
556 | ||
557 | %% propagate_idl_solution_to_bindings_bt(+IdlGraphMut, +SmtBindings). | |
558 | propagate_idl_solution_to_bindings_bt(IdlGraphMut, SmtBindings) :- | |
559 | get_preference(dpllt_use_idl_theory_solver,true), | |
560 | !, | |
561 | % TO DO: try the current solution first | |
562 | ( get_candidate_bounds_from_non_idl_constraints(CandidateTuples), | |
563 | difference_logic_solver:try_candidate_bounds(IdlGraphMut, CandidateTuples, Result) | |
564 | ; difference_logic_solver:get_all_solutions_on_bt(IdlGraphMut, Result) | |
565 | ), | |
566 | Result = solution(Bindings), | |
567 | set_bindings(Bindings, SmtBindings). | |
568 | propagate_idl_solution_to_bindings_bt(_, _). | |
569 | ||
570 | %% propagate_fd_bounds_to_idl_solver(+IdlGraphMut, +SmtBindings). | |
571 | propagate_fd_bounds_to_idl_solver(IdlGraphMut, SmtBindings) :- | |
572 | get_preference(dpllt_use_idl_theory_solver,true), | |
573 | !, | |
574 | difference_logic_solver:get_registered_vars(IdlGraphMut, Vars), | |
575 | get_constraints_from_fd_bounds(SmtBindings, Vars, [], ConjList), | |
576 | difference_logic_solver:register_constraints(IdlGraphMut, ConjList). | |
577 | propagate_fd_bounds_to_idl_solver(_, _). | |
578 | ||
579 | get_constraints_from_fd_bounds(_, [], Acc, Acc). | |
580 | get_constraints_from_fd_bounds(SmtBindings, [Var|T], Acc, ConjList) :- | |
581 | memberchk(bind(Var, int(Int)), SmtBindings), | |
582 | integer(Int), | |
583 | !, | |
584 | Equality = b(equal(b(identifier(Var),integer,[]),b(integer(Int),integer,[])),pred,[]), | |
585 | get_constraints_from_fd_bounds(SmtBindings, T, [Equality|Acc], ConjList). | |
586 | get_constraints_from_fd_bounds(SmtBindings, [Var|T], Acc, ConjList) :- | |
587 | memberchk(bind(Var, int(FDVar)), SmtBindings), | |
588 | fd_var(FDVar), | |
589 | fd_min(FDVar, Min), | |
590 | fd_max(FDVar, Max), | |
591 | !, | |
592 | ( var_geq_min(Var, Min, MinBound) | |
593 | -> ( var_leq_max(Var, Max, MaxBound) | |
594 | -> NAcc = [b(conjunct(MinBound,MaxBound),pred,[])|Acc] | |
595 | ; NAcc = [MinBound|Acc] | |
596 | ) | |
597 | ; ( var_leq_max(Var, Max, MaxBound) | |
598 | -> NAcc = [MaxBound|Acc] | |
599 | ; NAcc = Acc | |
600 | ) | |
601 | ), | |
602 | get_constraints_from_fd_bounds(SmtBindings, T, NAcc, ConjList). | |
603 | get_constraints_from_fd_bounds(SmtBindings, [_|T], Acc, ConjList) :- | |
604 | get_constraints_from_fd_bounds(SmtBindings, T, Acc, ConjList). | |
605 | ||
606 | var_geq_min(Var, Min, MinBound) :- | |
607 | % -v <= -min | |
608 | integer(Min), | |
609 | Min1 is Min * -1, | |
610 | MinBound = b(less_equal(b(minus(b(identifier('_zero'),integer,[]),b(identifier(Var),integer,[])),integer,[]),b(integer(Min1),integer,[])),pred,[]). | |
611 | ||
612 | var_leq_max(Var, Max, MaxBound) :- | |
613 | % v <= Max | |
614 | integer(Max), | |
615 | MaxBound = b(less_equal(b(identifier(Var),integer,[]),b(integer(Max),integer,[])),pred,[]). | |
616 | ||
617 | get_candidate_bounds_from_non_idl_constraints(CandidateTuples) :- | |
618 | findall((VarName,Candidates), idl_candidate_constants(VarName, Candidates), CandidateTuples). | |
619 | ||
620 | set_bindings([], _). | |
621 | set_bindings([binding(VarName,Val,_)|T], SmtBindings) :- | |
622 | member(bind(VarName,Val), SmtBindings), | |
623 | set_bindings(T, SmtBindings). | |
624 | ||
625 | %% exclude_idl_solution(+IdlBindings, +SmtBindings, +WfStoreSmt). | |
626 | exclude_idl_solution(IdlBindings, SmtBindings, WfStoreSmt) :- | |
627 | initial_solution, | |
628 | exclude_solution(IdlBindings, Exclusion), | |
629 | b_test_boolean_expression(Exclusion, SmtBindings, [], WfStoreSmt). | |
630 | exclude_idl_solution(_, _, _) :- | |
631 | \+ initial_solution. | |
632 | ||
633 | %% exclude_solution(+Bindings, -Exclusion). | |
634 | exclude_solution([], b(truth,pred,[])). | |
635 | exclude_solution([Binding|T], Exclusion) :- | |
636 | get_equality_from_binding(Binding, EQ), | |
637 | exclude_solution(T, EQ, Exclusion). | |
638 | ||
639 | exclude_solution([], Acc, Acc). | |
640 | exclude_solution([Binding|T], Acc, Exclusion) :- | |
641 | get_equality_from_binding(Binding, EQ), | |
642 | NAcc = b(disjunct(b(negation(EQ),pred,[]),Acc),pred,[]), | |
643 | exclude_solution(T, NAcc, Exclusion). | |
644 | ||
645 | %% get_equality_from_binding(+Binding, -EQ). | |
646 | get_equality_from_binding(Binding, EQ) :- | |
647 | ( Binding = bind(VarName,Val) | |
648 | ; Binding = binding(VarName,Val,_) | |
649 | ), | |
650 | infer_value_type(Val, Type), | |
651 | EQ = b(equal(b(identifier(VarName),Type,[]),b(value(Val),Type,[])),pred,[]). | |
652 | ||
653 | %% setup_theory_wf_store(+SmtVars, -SmtBindings, -WFStoreSMT). | |
654 | setup_theory_wf_store(SmtVars, SmtBindings, WFStoreSMT) :- | |
655 | set_up_typed_localstate(SmtVars, _, SmtTypedVals, [], SmtBindings, positive), | |
656 | init_wait_flags(WFStoreSMT, [wf_smt_cdcl]), | |
657 | b_tighter_enumerate_all_values(SmtTypedVals, WFStoreSMT),!. | |
658 | ||
659 | log_solution :- | |
660 | \+ initial_solution, | |
661 | !, | |
662 | asserta(initial_solution). | |
663 | log_solution. | |
664 | ||
665 | /* currently not used: | |
666 | unfold_let(Pred, Unfolded) :- | |
667 | unfold_let([], Pred, Unfolded). | |
668 | ||
669 | unfold_let(IdTuples, b(identifier(Name),_,_), Replacement) :- | |
670 | member((b(identifier(Name),_,_),Ast), IdTuples), | |
671 | !, | |
672 | Replacement = Ast. | |
673 | unfold_let(IdTuples, b(Node,_,_), Replacement) :- | |
674 | ( Node = let_predicate(Ids,Asts,Body) | |
675 | ; Node = let_expression(Ids,Asts,Body) | |
676 | ), | |
677 | !, | |
678 | maplist(unfold_let(IdTuples), Asts, NAsts), | |
679 | zip_acc(Ids, NAsts, IdTuples, NewIdTuples), % TODO: ideally remove IdTuples which are clashing with Ids | |
680 | unfold_let(NewIdTuples, Body, Replacement). | |
681 | unfold_let(IdTuples, b(Node,Type,Info), Replacement) :- | |
682 | syntaxtransformation(Node,Subs,Names,NSubs,NewNode),!, | |
683 | Replacement = b(NewNode,Type,Info), | |
684 | exclude(hidden_by_local_var(Names),IdTuples,NIdTuples), | |
685 | l_unfold_let(Subs,NIdTuples,NSubs). | |
686 | unfold_let(I, Ast, _) :- add_internal_error('Not a typed expression:', unfold_let(I,Ast,_)),fail. | |
687 | ||
688 | hidden_by_local_var(Names,(TID,_)) :- def_get_texpr_id(TID,ID), member(ID,Names). | |
689 | ||
690 | l_unfold_let([],_,[]). | |
691 | l_unfold_let([H|T],IdTuples,[NH|NT]) :- | |
692 | unfold_let(H,IdTuples,NH), | |
693 | l_unfold_let(T,IdTuples,NT). | |
694 | ||
695 | zip_acc([], [], Acc, Acc). | |
696 | zip_acc([A|T1], [B|T2], Acc, Zipped) :- | |
697 | zip_acc(T1, T2, [(A,B)|Acc], Zipped). % TODO: should we just store ID and not typed ID in A? | |
698 | ||
699 | */ | |
700 | ||
701 | %% For debugging: transform bool formula back to SMT | |
702 | /*bool_formula_to_smt(b(truth,pred,I), b(truth,pred,I)). | |
703 | bool_formula_to_smt(b(falsity,pred,I), b(falsity,pred,I)). | |
704 | bool_formula_to_smt(b(equal(Id,Bool),pred,_), Smt) :- | |
705 | Id = b(identifier(_),boolean,IdInfo), | |
706 | member(smt_formula(TSmt), IdInfo), | |
707 | !, | |
708 | ( Bool = b(boolean_true,boolean,_) | |
709 | -> Smt = TSmt | |
710 | ; Smt = b(negation(TSmt),pred,[]) | |
711 | ). | |
712 | bool_formula_to_smt(b(equal(Id,Bool),pred,I), Out) :- % bool ids from Tseitin optimization | |
713 | !, | |
714 | Out = b(equal(Id,Bool),pred,I). | |
715 | bool_formula_to_smt(b(conjunct(A,B),pred,I), Out) :- | |
716 | !, | |
717 | bool_formula_to_smt(A, SmtA), | |
718 | bool_formula_to_smt(B, SmtB), | |
719 | Out = b(conjunct(SmtA,SmtB),pred,I). | |
720 | bool_formula_to_smt(b(disjunct(A,B),pred,I), Out) :- | |
721 | !, | |
722 | bool_formula_to_smt(A, SmtA), | |
723 | bool_formula_to_smt(B, SmtB), | |
724 | Out = b(disjunct(SmtA,SmtB),pred,I).*/ | |
725 | %% |