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(unsat_cores,[unsat_core/2, |
7 | | unsat_core_list_with_time_limit/5, unsat_chr_core_list_with_auto_time_limit/5, |
8 | | unsat_core_wth_auto_time_limit/5, |
9 | | unsat_core_with_fixed_conjuncts/3, unsat_chr_core_with_fixed_conjuncts_auto_time_limit/4, |
10 | | minimum_unsat_core/2, |
11 | | minimum_unsat_core_with_fixed_conjuncts/3, |
12 | | tcltk_unsat_core_properties/3, unsat_core_properties_table/1, |
13 | | quick_bup_core_up_to/4, quick_bup_core/4]). |
14 | | |
15 | | :- use_module(library(terms),[term_size/2]). |
16 | | :- use_module(library(between),[between/3]). |
17 | | :- use_module(library(ordsets),[ord_intersect/2, ord_union/3]). |
18 | | :- use_module(library(lists),[min_member/3, reverse/2, select/3, last/2, maplist/2, maplist/3, nth1/3]). |
19 | | |
20 | | :- use_module(probsrc(bsyntaxtree)). |
21 | | :- use_module(probsrc(solver_interface), [solve_predicate/5]). |
22 | | :- use_module(probsrc(preferences), [get_preference/2]). |
23 | | :- use_module(probsrc(debug), [debug_println/2, debug_print/1,debug_format/3, silent_mode/1, set_silent_mode/1, formatsilent/2]). |
24 | | :- use_module(probsrc(self_check), [assert_must_succeed/1,assert_must_fail/1]). |
25 | | :- use_module(probsrc(translate),[translate_bexpression_with_limit/3, set_unicode_mode/0, unset_unicode_mode/0]). |
26 | | :- use_module(probsrc(specfile),[get_specification_description/2]). |
27 | | :- use_module(probsrc(error_manager),[get_tk_table_position_info/2,add_warning/4, add_message/4,add_internal_error/2]). |
28 | | :- use_module(probsrc(tools_strings),[ajoin/2]). |
29 | | :- use_module(probsrc(preferences), [temporary_set_preference/3, reset_temporary_preference/2]). |
30 | | :- use_module(probsrc(solver_interface), [solve_predicate/4]). |
31 | | :- use_module(probsrc(tools),[split_list/4]). |
32 | | |
33 | | :- use_module(cdclt_solver('cdclt_solver'), [cdclt_solve_predicate/3]). |
34 | | :- use_module(smt_solvers_interface(smt_solvers_interface),[smt_solve_predicate/5]). |
35 | | |
36 | | :- use_module(probsrc(module_information),[module_info/2]). |
37 | | :- module_info(group,cbc). |
38 | | :- module_info(description,'Computation of unsatisfiable cores of predicates.'). |
39 | | |
40 | | % possible options are: |
41 | | % - keep_exact_result: keep exact top-level result obtained for entire predicate (e.g., to find cause of time-out) |
42 | | % - no_warning_if_sol_found |
43 | | % - ignore_if_sol_found: potentially dangerous and one has to check manually that GlobalRes is a contradiction |
44 | | % - auto_time_out_factor(Factor) |
45 | | % - fixed_conjuncts(FixedList) |
46 | | % - branch_and_bound |
47 | | % - inspect_nested_conjuncts(BOOL) (default true) |
48 | | % - inspect_quantifiers(BOOL) (default true) |
49 | | |
50 | | |
51 | | :- assert_must_succeed((unsat_core(b(falsity,pred,[]),Core), (ground(Core), Core = b(falsity,pred,_)))). |
52 | | :- assert_must_succeed((unsat_core(b(conjunct(b(falsity,pred,[]),b(truth,pred,[])),pred,[]),Core), (ground(Core), Core = b(falsity,pred,_)))). |
53 | | |
54 | | % for meta_interface and ProB2: |
55 | | unsat_core_properties_table(list([list([PropsHeader,'Source'])|ResList])) :- |
56 | | get_preference(translation_limit_for_table_commands,Limit), |
57 | | get_specification_description(properties,PROPS), |
58 | | unsat_core_of_properties(CorePredicates,[no_warning_if_sol_found],StrRes), |
59 | | ajoin([PROPS,' : ',StrRes],PropsHeader), |
60 | | set_unicode_mode, |
61 | | call_cleanup(findall(list([TS,PosInfo]), |
62 | | (member(CP,CorePredicates), translate_bexpression_with_limit(CP,Limit,TS), |
63 | | get_tk_table_position_info(CP,PosInfo)),ResList), |
64 | | unset_unicode_mode). |
65 | | |
66 | | % HasUCLabels is true if the CorePredicates have UnsatCore special labels like @TO, @WD, ... |
67 | | tcltk_unsat_core_properties(list(CorePredStrings),HasUCLabels,StrRes) :- |
68 | | % TO DO: transmit runtime from Runtime to FINALISE SETUP_CONSTANTS to avoid re-checking Properties |
69 | | unsat_core_of_properties(CorePredicates,[],StrRes), |
70 | | set_unicode_mode, |
71 | | call_cleanup(maplist(translate:translate_bexpression,CorePredicates,CorePredStrings), |
72 | | unset_unicode_mode), |
73 | | (member(CP,CorePredicates), pred_has_unsat_core_label(CP,_) -> HasUCLabels=true ; HasUCLabels=false), |
74 | | maplist(add_warn,CorePredicates). % so that we see the spans of the unsat core in the edit window |
75 | | |
76 | | unsat_core_of_properties(CorePredicates,Options,StrRes) :- |
77 | | bmachine:b_get_properties_from_machine(Properties), % TO DO: also provide for unsat component |
78 | | debug_print('Computing (efficient) unsat core for PROPERTIES'),nl, |
79 | | unsat_chr_core_list_with_auto_time_limit(Properties,5000,Options,GlobalResult,CorePredicates), |
80 | | (explain_result(GlobalResult,Explanation) -> StrRes=Explanation ; StrRes='**UNKNOWN**'). |
81 | | |
82 | | add_warn(Conjunct) :- |
83 | | add_warning(unsat_cores,'This is part of an unsatisfiable core of the PROPERTIES clause: ',Conjunct,Conjunct). |
84 | | |
85 | | |
86 | | % TODO: cleanup / rewrite predicate in order to minimize cores |
87 | | % i.e. implications could be rewritten into conjuncts |
88 | | |
89 | | :- assert_must_fail((minimum_unsat_core(b(conjunct(b(falsity,pred,[]), |
90 | | b(conjunct(b(less(b(integer(1),integer,[]),b(identifier(x),integer,[])),pred,[]), |
91 | | b(less(b(identifier(x),integer,[]),b(integer(1),integer,[])),pred,[])),pred,[])),pred,[]),Core), |
92 | | (ground(Core), Core = b(less(b(integer(1),integer,[]),b(identifier(x),integer,[])),pred,_)))). |
93 | | :- assert_must_fail((minimum_unsat_core(b(conjunct(b(falsity,pred,[]), |
94 | | b(conjunct(b(less(b(integer(1),integer,[]),b(identifier(x),integer,[])),pred,[]),b(less(b(identifier(x),integer,[]),b(integer(1),integer,[])),pred,[])),pred,[])),pred,[]),Core), |
95 | | (ground(Core), Core = b(less(b(identifier(x),integer,[]),b(integer(1),integer,[])),pred,_)))). |
96 | | % there should be two cores |
97 | | :- assert_must_succeed((findall(Core, unsat_core(b(conjunct(b(falsity,pred,[]), |
98 | | b(conjunct(b(less(b(integer(1),integer,[]),b(identifier(x),integer,[])),pred,[]),b(less(b(identifier(x),integer,[]),b(integer(1),integer,[])),pred,[])),pred,[])),pred,[]),Core), |
99 | | ListOfCores), length(ListOfCores,2))). % was 5 |
100 | | |
101 | | % call if you know how much time (in ms) it took to find inconsistency in Predicate |
102 | | unsat_core_list_with_time_limit(Predicate,TimeToFindContradiction,Options,GlobalRes, CorePredicates) :- |
103 | | temporary_set_timeout(TimeToFindContradiction,CHNG1), |
104 | | % this time will be multiplied by 2 below |
105 | ? | call_cleanup(unsat_core_list(Predicate,Options,GlobalRes,CorePredicates), |
106 | | reset_temporary_preference(time_out,CHNG1)). |
107 | | |
108 | | |
109 | | unsat_chr_core_list_with_auto_time_limit(Predicate,MaxTime,Options,GlobalRes,CorePredicates) :- |
110 | | temporary_set_preference(use_chr_solver,true,CHNG1), |
111 | ? | call_cleanup(unsat_core_wth_auto_time_limit(Predicate,MaxTime,Options,GlobalRes,CorePredicates), |
112 | | reset_temporary_preference(use_chr_solver,CHNG1)). |
113 | | |
114 | | % automatically determine time limit by checking that full predicate is false and registering time |
115 | | unsat_core_wth_auto_time_limit(Predicate,MaxTime,Options,GlobalRes,CorePredicates) :- |
116 | | % TODO: if unsat_core_target(.) provided and fixed time-out then we do not need to compute this: |
117 | ? | get_time_out_for_comp_pred(Predicate,_Nr,CompPred,TimeToFindContradiction,Options,GlobalRes), |
118 | | progress_info(GlobalRes), |
119 | | (solution_found(GlobalRes) |
120 | | -> ( member(no_warning_if_sol_found,Options) |
121 | | -> add_message(unsat_cores,'Predicate has a solution, unsat core cannot be computed: ',Predicate,Predicate) |
122 | | ; member(ignore_if_sol_found,Options) |
123 | | -> true |
124 | | ; add_warning(unsat_cores,'Predicate has a solution, unsat core cannot be computed: ',Predicate,Predicate) |
125 | | ), |
126 | | CorePredicates=[Predicate] |
127 | ? | ; (member(auto_time_out_factor(Factor),Options) -> true ; Factor=120), |
128 | | Limit0 is min(((TimeToFindContradiction*Factor)/100),MaxTime), %print(time_limit(Limit)),nl, |
129 | ? | (member(min_time_out(MinTime),Options) -> true ; MinTime = 5), |
130 | | Limit is max(MinTime,Limit0), |
131 | | (member(unsat_core_target(Target),Options) -> true ; Target = GlobalRes), |
132 | | % with unsat_core_target(contradiction_found) we can look for real contradictions even if the whole predicate has a time-out or translation error |
133 | | debug_format(19,'Time out: ~w ms, target for unsat core: ~w~n',[Limit,Target]), |
134 | ? | unsat_core_list_with_time_limit(CompPred,Limit,Options,Target,CorePredicates) |
135 | | ). |
136 | | |
137 | | %get_time_out_for_comp_pred(SubPred,1,SubPred,TimeToFindContradiction,Options,Res) :- !,get_time_out_for_pred(SubPred,TimeToFindContradiction,Options,Res). |
138 | | get_time_out_for_comp_pred(Predicate,Nr,SubPred,TimeToFindContradiction,Options,Res) :- % partition |
139 | | temporary_set_preference(partition_predicates_inline_equalities,false,OldValueOfPref), % avoid inlining equalities |
140 | | % TO DO: maybe provide an option for this |
141 | | call_cleanup(predicate_components(Predicate,Components), |
142 | | reset_temporary_preference(partition_predicates_inline_equalities, OldValueOfPref)), |
143 | ? | if( (nth1(Nr,Components,component(SubPred,Ids)), % select component via backtracking |
144 | | debug_println(19,examining_component(Nr,Ids)), |
145 | | get_time_out_for_pred(SubPred,TimeToFindContradiction,Options,Res), |
146 | | progress_info(Res), |
147 | | %print(sol_comp(Nr,Res)),nl, |
148 | | % TODO: if translation_or_setup_failed and target is contradiction_found: filter predicate to that part that can be translated |
149 | | \+ solution_found(Res)), |
150 | | % we have found a sub-component that has a contradiction |
151 | | true, |
152 | | (SubPred=Predicate, |
153 | | get_time_out_for_pred(SubPred,TimeToFindContradiction,Options,Res) |
154 | | ) |
155 | | ). |
156 | | |
157 | | get_time_out_for_pred(Predicate,TimeToFindContradiction,Options,Res) :- |
158 | | silent_mode(CurMode), set_silent_mode(on), |
159 | | statistics(walltime,[T1,_]), |
160 | | solve_core_pred(Predicate, 2, Options, Res), |
161 | | statistics(walltime,[T2,_]), TimeToFindContradiction is T2-T1, |
162 | | set_silent_mode(CurMode), !. |
163 | | |
164 | | temporary_set_timeout(TimeToFindContradiction,CHNG1) :- |
165 | | number(TimeToFindContradiction), TimeToFindContradiction>=0, |
166 | | NewTimeOut is round(TimeToFindContradiction+10), % in case we have 0; just add a few ms |
167 | | temporary_set_preference(time_out,NewTimeOut,CHNG1). |
168 | | |
169 | | unsat_core(Predicate,CorePredicate) :- |
170 | | unsat_core(Predicate,no_solution_found,CorePredicate). |
171 | | unsat_core(Predicate,GlobalResult,CorePredicate) :- |
172 | | unsat_core_list(Predicate,[],GlobalResult,CorePreds), |
173 | | conjunct_predicates(CorePreds,CorePredicate). |
174 | | |
175 | | unsat_core_list(Predicate,Options,GlobalResult,CorePreds) :- |
176 | | %print('COMPUTING CORE:'),nl,translate:nested_print_bexpr(Predicate),nl, |
177 | | %(member(fixed_conjuncts(FL),Options) -> print('FIXED CONJ:'),nl,translate:nested_print_bexpr(FL),nl ; true),nl, |
178 | | uc_conjunction_to_list(Predicate,Options,PredicateList), |
179 | | length(PredicateList,OrigLen), |
180 | | silent_mode(CurMode), set_silent_mode(on), |
181 | ? | call_cleanup(unsat_core_with_options_lists(PredicateList,Options,GlobalResult,CorePreds), |
182 | | set_silent_mode(CurMode)), |
183 | | ( CurMode = on |
184 | | -> true |
185 | | ; length(CorePreds,NewLen), |
186 | | debug_format(5, '~nFound core with ~w/~w conjuncts.~n', [NewLen,OrigLen]) |
187 | | ). |
188 | | |
189 | | % strip outermost existential quantifiers so that we can examine the conjunction inside |
190 | | uc_conjunction_to_list(b(exists(_V,Pred),pred,_), Options, List) :- |
191 | | member(inspect_quantifiers(true), Options), |
192 | | !, |
193 | | conjunction_to_list(Pred, List). |
194 | | % TO DO: add let_predicate (decomposed further below) ? forall ? |
195 | | uc_conjunction_to_list(Pred, _, List) :- conjunction_to_list(Pred, List). |
196 | | |
197 | | % called by ProB2 |
198 | | unsat_chr_core_with_fixed_conjuncts_auto_time_limit(Predicate,FixedConjuncts,MaxTimeLimit,CorePredicate) :- |
199 | | temporary_set_preference(use_chr_solver,true,CHNG1), % TODO: put into options ? |
200 | | Options = [], |
201 | | call_cleanup(unsat_core_with_fixed_conjuncts_auto_time_limit(Predicate,FixedConjuncts,MaxTimeLimit,Options,CorePredicate), |
202 | | reset_temporary_preference(use_chr_solver,CHNG1)). |
203 | | unsat_core_with_fixed_conjuncts_auto_time_limit(Predicate,FixedConjuncts,MaxTimeLimit,Options,CorePredicate) :- |
204 | | is_truth(FixedConjuncts),!, |
205 | | Options=[], |
206 | | unsat_core_wth_auto_time_limit(Predicate,MaxTimeLimit,Options,_,CorePreds), |
207 | | conjunct_predicates(CorePreds,CorePredicate). |
208 | | unsat_core_with_fixed_conjuncts_auto_time_limit(Predicate,FixedConjuncts,MaxTimeLimit,Options,CorePredicate) :- |
209 | | conjunct_predicates([Predicate,FixedConjuncts],FullPred), |
210 | | get_time_out_for_pred(FullPred,TimeToFindContradiction,Options,GlobalRes), % we cannot use component version due to FixedConjuncts |
211 | | progress_info(GlobalRes), |
212 | | (solution_found(GlobalRes) |
213 | | -> add_warning(unsat_cores,'Predicate has a solution, unsat core cannot be computed: ',Predicate,Predicate), |
214 | | CorePredicate=Predicate |
215 | | ; Limit is min((TimeToFindContradiction*110/100),MaxTimeLimit), |
216 | | temporary_set_timeout(Limit,CHNG1), |
217 | | call_cleanup(unsat_core_with_fixed_conjuncts(Predicate,FixedConjuncts,GlobalRes,CorePredicate), |
218 | | reset_temporary_preference(time_out,CHNG1)) |
219 | | ). |
220 | | |
221 | ? | unsat_core_with_fixed_conjuncts(P,FC,Core) :- unsat_core_with_fixed_conjuncts(P,FC,no_solution_found,Core). |
222 | | unsat_core_with_fixed_conjuncts(Predicate,FixedPreds,GlobalRes,CorePredicate) :- |
223 | | conjunction_to_list(FixedPreds,FixedList), |
224 | | Options = [fixed_conjuncts(FixedList)], % should we set inspect_nested_conjuncts(false) ? |
225 | ? | unsat_core_list(Predicate,Options,GlobalRes,CorePreds), |
226 | | conjunct_predicates(CorePreds,CorePredicate). |
227 | | |
228 | | unsat_core_with_options_lists(PredicateList,Options,GlobalResult,CorePreds) :- |
229 | | get_preference(unsat_core_algorithm,divide_and_conquer), !, % TO DO: pass GlobalResult |
230 | | temporary_set_preference(allow_enumeration_of_infinite_types,false,OldValueOfPref), |
231 | | (member(fixed_conjuncts(FixedList),Options) -> true ; FixedList=[]), |
232 | | call_cleanup(unsat_core_fixed_conjuncts_lists_divide_and_conquer(PredicateList,FixedList,GlobalResult,Options,CorePreds), |
233 | | reset_temporary_preference(allow_enumeration_of_infinite_types,OldValueOfPref)). |
234 | | unsat_core_with_options_lists(PredicateList,Options,GlobalResult,CorePreds) :- |
235 | | reset_max_core_size, |
236 | | temporary_set_preference(allow_enumeration_of_infinite_types,true, OldValueOfPref), |
237 | | %print('START: '),nl,translate:nested_print_bexpr(RPredicateList),nl, |
238 | ? | call_cleanup(unsat_core_with_options_linear(PredicateList,Options, GlobalResult, CorePreds), |
239 | | reset_temporary_preference(allow_enumeration_of_infinite_types, OldValueOfPref)), |
240 | ? | (member(branch_and_bound,Options) -> length(CorePreds,Len), set_max_core_size(Len) ; true). |
241 | | |
242 | | |
243 | | unsat_core_with_options_linear(PredicateList,Options, GlobalRes, CorePreds) :- |
244 | | reverse(PredicateList, RPredicateList), % so that we properly treat WD we start removing from the back |
245 | ? | compute_cores_linear(RPredicateList, Options, GlobalRes, CorePreds). |
246 | | |
247 | | compute_cores_linear(RPredicateList, Options, GlobalRes, CorePreds) :- |
248 | | compute_core_linear_loop(RPredicateList, 0, [], Options, GlobalRes, InCore,NotInCore,AllPreds),!, |
249 | | ( CorePreds = InCore |
250 | | ; % find more cores: |
251 | | debug_print('R'), |
252 | ? | restart_core_linear(InCore,NotInCore,AllPreds,Options,GlobalRes,CorePreds) |
253 | | ). |
254 | | |
255 | | % restart the unsat core algorithm with one conjunct from core removed from the original predicate list |
256 | | restart_core_linear([C|Cs],Keep,AllPreds,Options,GlobalRes,CorePreds) :- |
257 | | append(Keep,Cs,LL0), add_fixed_conj(Options,LL0,LL), |
258 | | conjunct_predicates(LL, ConjunctionToCheck), |
259 | | % print('TRY TO REMOVE CORE CONJUNCT FOR RESTART: '), translate:print_bexpr(C),nl, |
260 | | solve_core_pred(ConjunctionToCheck, 1, Options, Res), |
261 | | progress_info(Res), |
262 | | must_keep_conjunct(Res,GlobalRes,Options),!, |
263 | | % we need to keep C in PredicateList for restart; otherwise we change global result |
264 | ? | restart_core_linear(Cs,[C|Keep],AllPreds,Options,GlobalRes,CorePreds). |
265 | | restart_core_linear([C|_],_,AllPreds,Options,GlobalRes,CorePreds) :- |
266 | | % we can remove C from the global list |
267 | | (\+ get_preference(unsat_core_algorithm,divide_and_conquer) -> ! ; true), % linear_greedy |
268 | | remove_conjunct(C,AllPreds,NewAllPreds), % remove C, keeping original order |
269 | | debug_print('C'), |
270 | ? | compute_cores_linear(NewAllPreds, Options, GlobalRes, CorePreds). |
271 | | restart_core_linear([C|Cs],Keep,AllPreds,Options,GlobalRes,CorePreds) :- |
272 | | % try to remove another conjunct; we may find the same core multiple times |
273 | | restart_core_linear(Cs,[C|Keep],AllPreds,Options,GlobalRes,CorePreds). |
274 | | |
275 | | add_fixed_conj(Options,P,Res) :- member(fixed_conjuncts(FixedList),Options),!, |
276 | | (append(FixedList,P,FP) -> Res=FP |
277 | | ; add_internal_error('Illegal fixed list: ',FixedList), Res=P). |
278 | | add_fixed_conj(_,P,P). |
279 | | |
280 | | remove_conjunct(C,RPredicateList,NewRPredicateList) :- |
281 | ? | select(CC,RPredicateList,NewRPredicateList), % TO DO: remember position of C and use nth1 |
282 | | same_texpr(C,CC),!. |
283 | | remove_conjunct(C,RPredicateList,New) :- |
284 | | add_internal_error('Could not find conjunct from unsat core: ',remove_conjunct(C,RPredicateList,New)),fail. |
285 | | |
286 | | |
287 | | % implies_optional_hyp(C,DefinitelyInCore,_Hyp) |
288 | | % implied_by_core(C,DefinitelyInCore) |
289 | | % implies_optional_hyp(C,DefinitelyInCore,_Hyp) |
290 | | |
291 | | % simple unsat core computation: remove conjuncts till satisfiability is reached |
292 | | compute_core_linear_loop([], _,DefinitelyInCore, _Options, _, DefinitelyInCore,[],[]). |
293 | | compute_core_linear_loop([C|Cs], Counter,DefinitelyInCore, Options, GlobalRes, CorePreds,NotInCore,AllPreds) :- |
294 | | % remove C and check for satisfiability |
295 | | append(DefinitelyInCore,Cs,LL0), add_fixed_conj(Options,LL0,LL), |
296 | | conjunct_predicates(LL, ConjunctionToCheck), |
297 | | % print('TRY TO REMOVE: '), translate:print_bexpr(C),nl, |
298 | | solve_core_pred(ConjunctionToCheck, 1, Options, Res), |
299 | | debug_println(4, solve_predicate_result(Res)), |
300 | | progress_info(Res), |
301 | | must_keep_conjunct(Res,GlobalRes,Options), % a solution has been found or a time-out occured - C can not be removed; |
302 | | % normally corresponds to solve_result: contradiction_found or no_solution_found(unfixed_deferred_sets) |
303 | | !, |
304 | | % print('KEEPING: '), translate:print_bexpr(C),nl, |
305 | | % TO DO: when looking for minimal unsat core we could provide an upper-bound for the length |
306 | | increase_core_counter(Counter,Options,IncCtr), |
307 | ? | (can_be_further_decomposed(C, CA, CB), |
308 | | (member(inspect_nested_conjuncts(INC),Options) -> INC=true ; true) |
309 | | -> % print('% Split: '), translate:print_bexpr(CA),nl, print('% '),translate:print_bexpr(CB),nl, |
310 | | compute_core_linear_loop([CA,CB|Cs], Counter,DefinitelyInCore, Options, GlobalRes, CorePreds,NotInCore,AllPreds) |
311 | | ; add_uc_info(Res, C, IC), AllPreds = [C|TAP], |
312 | | append(DefinitelyInCore,[IC],NewDefInCore), % TODO: use DCG |
313 | | compute_core_linear_loop(Cs, IncCtr,NewDefInCore, Options, GlobalRes, CorePreds,NotInCore,TAP) |
314 | | ). |
315 | | compute_core_linear_loop([C|Cs], Ctr, DefinitelyInCore, Options, GlobalRes, CorePreds,NotInCore,[C|AllPreds]) :- |
316 | | % format('CAN BE REMOVED (~w): ',[GlobalRes]), translate:print_bexpr(C),nl, |
317 | | % C can be removed - we no longer introduce a choice point to not remove C; further cores are found by restarting |
318 | | NotInCore = [C|RestNotInCore], |
319 | | compute_core_linear_loop(Cs, Ctr, DefinitelyInCore, Options, GlobalRes, CorePreds,RestNotInCore,AllPreds). |
320 | | |
321 | | :- dynamic max_core_size/1. |
322 | | increase_core_counter(Cur,Options,_) :- max_core_size(Max), Cur >= Max, |
323 | ? | member(branch_and_bound,Options),!,format('B~w',[Max]),fail. |
324 | | increase_core_counter(Cur,_,C1) :- C1 is Cur+1. |
325 | | |
326 | | reset_max_core_size :- retractall(max_core_size(_)). |
327 | | set_max_core_size(Max) :- retractall(max_core_size(_)), assertz(max_core_size(Max)). |
328 | | |
329 | | % check if an optional hypothesis in the current core is implied by the rest and the new hyp C |
330 | | %implies_optional_hyp(C,CurrentCore,Hyp) :- |
331 | | % \+ is_truth(CurrentCore), |
332 | | % select(Hyp,CurrentCore,Rest), % DO THIS TEST also for C which must be kept |
333 | | % bsyntaxtree:get_texpr_info(Hyp,IL), member(optionally_in_core,IL), |
334 | | % % print(' --> '),translate:print_bexpr(Hyp), print(' <== '),translate:print_bexpr(Rest),nl, |
335 | | % prove_sequent(animation,Hyp,[C|Rest]). |
336 | | % |
337 | | %:- use_module(wdsrc(well_def_analyser),[prove_sequent/3]). |
338 | | %implied_by_core(C,CurrentCore) :- prove_sequent(animation,C,CurrentCore). |
339 | | |
340 | | |
341 | | add_uc_info(SolverRes, C, IC2) :- |
342 | | ( ( ground(SolverRes), |
343 | | SolverRes = solution(_), Labels = [], |
344 | | UCInfo = [description('definitely in unsat core (solution)')]) |
345 | | ; ( SolverRes == error, Labels = ['WD'], |
346 | | UCInfo = [description('definitely in unsat core (kept to prevent WD error)')]) |
347 | | ; ( SolverRes == time_out, Labels = ['TO'], % pretty printed as @TO |
348 | | UCInfo = [description('possibly in unsat core (kept to prevent timeout)')]) |
349 | | ; ( ground(SolverRes), Labels = ['VTO'], |
350 | | SolverRes = no_solution_found(enumeration_warning(_,_,_,_,_)), |
351 | | UCInfo = [description('possibly in unsat core (kept to prevent virtual timeout)')]) |
352 | | ; ( SolverRes == no_solution_found(unfixed_deferred_sets), Labels = ['DEFSETS'], |
353 | | UCInfo = [description('definitely in unsat core (kept to fix deferred sets)')]) |
354 | | ; ( ground(SolverRes), Labels = ['UNKNOWN'], |
355 | | SolverRes = no_solution_found(_), |
356 | | UCInfo = [description('possibly in unsat core (kept to prevent unknown solver result)')]) |
357 | | ), !, |
358 | | add_texpr_infos(C, UCInfo, IC1), |
359 | | add_labels_to_texpr(IC1,Labels,IC2). |
360 | | |
361 | | uc_label('WD'). |
362 | | uc_label('TO'). |
363 | | uc_label('VTO'). |
364 | | uc_label('DEFSETS'). |
365 | | |
366 | | pred_has_unsat_core_label(Pred,Label) :- get_texpr_label(Pred,Label), uc_label(Label). |
367 | | |
368 | | solution_found(Res) :- |
369 | | Res \= contradiction_found, |
370 | | Res \= no_solution_found(_), |
371 | | Res \= time_out, |
372 | | Res \= error, |
373 | | Res \= clpfd_overflow. |
374 | | |
375 | | % check if a removed conjunct has to be kept in unsat core given solver_result (w/o conjunct) and global result |
376 | | % possible value solution(_), contradiction_found, no_solution_found, ... |
377 | | must_keep_conjunct(solution(_),_,_) :- !. |
378 | | must_keep_conjunct(X,X,_) :- !,fail. % same result as global result, e.g., WD error or time_out |
379 | | must_keep_conjunct(_,_,Options) :- member(keep_exact_result,Options),!. |
380 | | must_keep_conjunct(contradiction_found,_,_) :- !,fail. % |
381 | | must_keep_conjunct(no_solution_found(X),GlobalRes,_) :- !, |
382 | | must_keep_no_sol_found(X,GlobalRes). |
383 | | must_keep_conjunct(_,_,_). |
384 | | |
385 | | must_keep_no_sol_found(enumeration_warning(_,_,_,_,critical),_). |
386 | | % relevant for e.g. :core f={1|->1} & x:dom(f) & f(x)>1 with -p TRY_FIND_ABORT TRUE |
387 | | must_keep_no_sol_found(solver_answered_unknown,_Global). % Global typically contradiction_found |
388 | | % TODO: look if there are other reasons where we should keep the conjunct |
389 | | |
390 | ? | can_be_further_decomposed(TE,A,B) :- can_be_further_decomposed(TE,positive,A,B). |
391 | ? | can_be_further_decomposed(b(E,pred,Info),NegPos,A,B) :- can_be_further_decomposed_aux(E,Info,NegPos,A,B). |
392 | | can_be_further_decomposed_aux(conjunct(A,B),_,positive,A,B). |
393 | | can_be_further_decomposed_aux(disjunct(A,B),_,negative,A,B). |
394 | | can_be_further_decomposed_aux(disjunct(A,B),_,positive,D1,D2) :- |
395 | | get_preference(unsat_core_algorithm,linear_greedy_decompose),!, % can blow up number of cases |
396 | | (can_be_further_decomposed(A,positive,A1,A2) % here we could try divide conjuncts in middle |
397 | | -> disjunct_predicates([A1,B],D1), |
398 | | disjunct_predicates([A2,B],D2) |
399 | | ; can_be_further_decomposed(B,positive,B1,B2) |
400 | | -> disjunct_predicates([A,B1],D1), |
401 | | disjunct_predicates([A,B2],D2) |
402 | | ). |
403 | | % above rule works for :core ((x=2&y=3) or (x=1&z=3)) & x>2 |
404 | | % for :core ((x=2 & y=3) or (x=3 & y=4)) & x>3 it sort of works but keeps both x/y |
405 | | can_be_further_decomposed_aux(implication(A,B),_,positive,I1,I2) :- |
406 | | (can_be_further_decomposed(B,positive,B1,B2) % A=>B1&B2 <--> A=>B1 & A=>B2 |
407 | | -> create_implication(A,B1,I1), |
408 | | create_implication(A,B2,I2) |
409 | | ; can_be_further_decomposed(A,negative,A1,A2) % (A1 or A2) => B <--> A1 => B & A2 => B |
410 | | -> create_implication(A1,B,I1), |
411 | | create_implication(A2,B,I2)). |
412 | | can_be_further_decomposed_aux(implication(A,B),_,negative,NA,B) :- % A=>B <--> not(A) or B |
413 | | create_negation(A,NA). |
414 | | can_be_further_decomposed_aux(negation(D),_,NegPos,NA,NB) :- neg_context(NegPos,PosNeg), |
415 | ? | can_be_further_decomposed(D,PosNeg,A,B), % not(A &/or B) <--> not(A) or/& not(B) |
416 | | create_negation(A,NA), |
417 | | create_negation(B,NB). |
418 | | can_be_further_decomposed_aux(let_predicate(V,E,P),Info,NegPos,LEA,LEB) :- |
419 | | can_be_further_decomposed(P,NegPos,A,B), |
420 | | LEA = b(let_predicate(V,E,A),pred,Info), % used_ids could be wrong; TO DO: think about exists |
421 | | LEB = b(let_predicate(V,E,B),pred,Info). |
422 | | can_be_further_decomposed_aux(lazy_let_pred(V,E,P),Info,NegPos,LEA,LEB) :- |
423 | | can_be_further_decomposed(P,NegPos,A,B), |
424 | | LEA = b(lazy_let_pred(V,E,A),pred,Info), |
425 | | LEB = b(lazy_let_pred(V,E,B),pred,Info). |
426 | | |
427 | | neg_context(positive,negative). neg_context(negative,positive). |
428 | | |
429 | | % a binary search algorithm in order to only need log(n) instead of n solver calls |
430 | | % Idea: split list of conjuncts in half, C = [A,B] |
431 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer([],_,_,_,_) :- !, fail. % we split too far! |
432 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer([C],_FixedConjuncts,_GlobalResult,_Options,[C]) :- |
433 | | !. % if there is only one conjunct left, it has to belong to the core |
434 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer(Clauses,FixedConjuncts,GlobalResult,Options,Core) :- |
435 | | split_in_half(Clauses,A,B), |
436 | | % print('SPLIT: '), print(A),nl,print(B),nl, |
437 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer_split(A,B,FixedConjuncts,GlobalResult,Options,Core). |
438 | | |
439 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer_split(A,B,FixedConjuncts,GlobalResult,Options,Core) :- |
440 | | append(A,FixedConjuncts,AllConjuncts), |
441 | | conjunct_predicates(AllConjuncts,APred), |
442 | | % print('SOLVING LHS: '), translate:print_bexpr(APred),nl, |
443 | | solve_core_pred(APred, 1, Options, ARealRes), |
444 | | progress_info(ARealRes), |
445 | | % print(result(ARealRes)),nl, |
446 | | !, % do not backtrack into solving or multiple results can be found leading to duplicated cores |
447 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer_recur1(ARealRes,A,B,FixedConjuncts,GlobalResult,Options,Core). |
448 | | |
449 | ? | progress_info(ARes) :- progress_info_aux(ARes),!,flush_output(user_output). |
450 | | progress_info_aux(time_out) :- progress_print('T'). |
451 | | progress_info_aux(error) :- progress_print('WD'). |
452 | | progress_info_aux(no_solution_found(E)) :- progress_info_no_sol_found_aux(E). |
453 | | progress_info_aux(contradiction_found) :- progress_print('-'). |
454 | | progress_info_aux(translation_or_setup_failed) :- progress_print('FAILED'). % Z3 or cvc4 translation failed |
455 | | progress_info_aux(solution(_)) :- progress_print('+'). |
456 | | progress_info_aux(clpfd_overflow) :- progress_print('O'). |
457 | | progress_info_aux(UNKNOWN) :- debug_format(5, '*~w*', [UNKNOWN]). |
458 | | |
459 | | %progress_print(C) :- !, write(C). |
460 | | progress_print(C) :- debug_print(C). |
461 | | |
462 | | progress_info_no_sol_found_aux(unfixed_deferred_sets) :- progress_print('-'). |
463 | | progress_info_no_sol_found_aux(solver_answered_unknown) :- progress_print('U'). |
464 | | progress_info_no_sol_found_aux(enumeration_warning(_,_,_,_,_)) :- progress_print('U(ENUM)'). |
465 | | progress_info_no_sol_found_aux(UNKNOWN) :- debug_format(5, '*~w*', [UNKNOWN]). |
466 | | |
467 | | explain_result(time_out,'UNKNOWN (TIME_OUT)'). |
468 | | explain_result(error,'UNKNOWN (ERROR)'). |
469 | | explain_result(clpfd_overflow,'UNKNOWN (CLPFD OVERFLOW)'). |
470 | | explain_result(no_solution_found(E),R) :- explain_no_sol(E,R). |
471 | | explain_result(contradiction_found,'FALSE'). |
472 | | explain_result(solution(_),'TRUE'). |
473 | | explain_result(_,'UNKNOWN'). |
474 | | |
475 | | explain_no_sol(solver_answered_unknown, 'UNKNOWN'). |
476 | | explain_no_sol(unfixed_deferred_sets,'FALSE (DEFERRED SETS)'). |
477 | | explain_no_sol(enumeration_warning(_,_,_,_,_), 'UNKNOWN (ENUMERATION WARNING)'). |
478 | | explain_no_sol(_, 'UNKNOWN'). |
479 | | |
480 | | definite_prob_result(solution(_),_). |
481 | | definite_prob_result(Res,Options) :- contradiction_result(Res,Options). |
482 | | |
483 | | solve_core_pred(BPred,_TOFactor,Options,Res) :- % translate:print_bexpr(BPred),nl, |
484 | | memberchk(try_prob_solver_first(TimeoutFactor),Options), |
485 | | solve_predicate(BPred,_State,TimeoutFactor,Res), |
486 | | formatsilent('ProB solve_predicate result : ~w~n',[Res]), |
487 | | definite_prob_result(Res,Options), !. |
488 | | solve_core_pred(BPred,TOFactor,Options,Res) :- % print(' --> Z3 SOLVE: '), translate:print_bexpr(BPred),nl, |
489 | | member(use_smt_solver(Solver),Options),!, |
490 | | Opts = [smt_time_out_factor(TOFactor), check_sat_skeleton(10), instantiate_quantifier_limit(5)], |
491 | | %formatsilent('Starting SMT solver ~w with ~w~n',[Solver,Opts]), |
492 | | smt_solve_predicate(Solver,Opts,BPred,_,Res), |
493 | | formatsilent('~w solve_predicate result : ~w~n',[Solver,Res]). |
494 | | solve_core_pred(BPred,_TOFactor,Options,Res) :- |
495 | | member(use_cdclt_solver,Options),!, % TODO: pass TOFactor to cdclt |
496 | | (cdclt_solve_predicate(BPred,_,Res) -> true |
497 | | ; Res= contradiction_found, |
498 | | add_internal_error('Solver call failed:',cdclt_solve_predicate(BPred,_,Res)) |
499 | | ). |
500 | | % TODO: add wd-prover |
501 | | solve_core_pred(BPred,TOFactor,Options,Res) :- |
502 | | (member(use_smt_mode/SMT,Options) -> true ; SMT=true), |
503 | | (member(use_clpfd_solver/CLPFD,Options) -> true ; CLPFD=true), |
504 | ? | (member(use_chr_solver/CHR,Options) -> T=[use_chr_solver/CHR] ; T=[]), |
505 | | solve_predicate(BPred,_BState,TOFactor,[use_smt_mode/SMT,use_clpfd_solver/CLPFD,ignore_wd_errors|T],Res). |
506 | | |
507 | | |
508 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer_recur1(ARealRes,A,_B,FixedConjuncts,GlobalResult,Options,Core) :- |
509 | | \+ must_keep_conjunct(ARealRes,GlobalResult,[]), % A is (virtually / effectively) unsat. Recur on A. |
510 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer(A,FixedConjuncts,GlobalResult,Options,Core). |
511 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer_recur1(ARealRes,A,B,FixedConjuncts,GlobalResult,Options,Core) :- |
512 | | append(B,FixedConjuncts,AllConjuncts), |
513 | | conjunct_predicates(AllConjuncts,BPred), |
514 | | % print('SOLVING RHS: '), translate:print_bexpr(BPred),nl, |
515 | | solve_core_pred(BPred,1,Options,BRealRes), |
516 | | progress_info(BRealRes), |
517 | | % print(result(BRealRes)),nl, |
518 | | !, |
519 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer_recur2(ARealRes,BRealRes,A,B,FixedConjuncts,GlobalResult,Options,Core). |
520 | | |
521 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer_recur2(_ARealRes,BRealRes,_A,B,FixedConjuncts,GlobalResult,Options,Core) :- |
522 | | \+ must_keep_conjunct(BRealRes,GlobalResult,[]), % B is (virtually / effectively) unsat. Recur on B. |
523 | | !, |
524 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer(B,FixedConjuncts,GlobalResult,Options,Core). |
525 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer_recur2(ARealRes,BRealRes,A,B,FixedConjuncts,GlobalResult,Options,Core) :- |
526 | | % the interesting case: if both are sat |
527 | | must_keep_conjunct(ARealRes,GlobalResult,[]), |
528 | | must_keep_conjunct(BRealRes,GlobalResult,[]), |
529 | | % minimize A assuming usat core includes B |
530 | | append(B,FixedConjuncts,FixedAndB), |
531 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer(A,FixedAndB,GlobalResult,Options,ACore), |
532 | | % now ACore & B is unsat and minimal but not minimum core |
533 | | % minimize B assuming ACore |
534 | | append(ACore,FixedConjuncts,FixedAndA), |
535 | | unsat_core_fixed_conjuncts_lists_divide_and_conquer(B,FixedAndA,GlobalResult,Options,BCore), |
536 | | append(ACore,BCore,Core). |
537 | | |
538 | | %divide_and_conquer_recur_condition(contradiction_found). |
539 | | %divide_and_conquer_recur_condition(no_solution_found(X)) :- |
540 | | % % to do: if the outermost conjunct already generates a time_out or critical enumeration warning then this should be enabled as a recur condition |
541 | | % X \= enumeration_warning(_,_,_,_,critical). |
542 | | %%divide_and_conquer_recur_condition(time_out). |
543 | | |
544 | | split_in_half(C, A, B) :- |
545 | | length(C,L), |
546 | | Half is L // 2, |
547 | | length(A, Half), |
548 | | append(A, B, C). |
549 | | |
550 | | % there is currently no known good algorithm to compute the minimum unsat core |
551 | | % we can just compute every core and take the minimum one out of the list |
552 | | :- assert_must_succeed((minimum_unsat_core(b(conjunct(b(falsity,pred,[]), |
553 | | b(conjunct(b(less(b(integer(1),integer,[]),b(identifier(x),integer,[])),pred,[]),b(less(b(identifier(x),integer,[]),b(integer(1),integer,[])),pred,[])),pred,[])),pred,[]),Core), |
554 | | (ground(Core), Core = b(falsity,pred,_)))). |
555 | | minimum_unsat_core(Predicate,CorePredicate) :- |
556 | | minimum_unsat_core_with_fixed_conjuncts(Predicate,[],CorePredicate). |
557 | | |
558 | | minimum_unsat_core_with_fixed_conjuncts(Predicate,FixedConjuncts,CorePredicate) :- |
559 | | Options = [branch_and_bound,fixed_conjuncts(FixedConjuncts)], |
560 | | findall(CoreList, |
561 | | unsat_core_list(Predicate,Options,no_solution_found,CoreList), |
562 | | Cores), |
563 | | reset_max_core_size, |
564 | | min_member(compare_length,MinCs,Cores), |
565 | | conjunct_predicates(MinCs,CorePredicate). |
566 | | |
567 | | compare_length(P1,P2) :- length(P1,L1), length(P2,L2), |
568 | | (L1<L2 -> true |
569 | | ; L1=L2 -> |
570 | | term_size(P1, T1), |
571 | | term_size(P2, T2), |
572 | | T1 < T2 |
573 | | ). |
574 | | |
575 | | % ---------------------------------- |
576 | | |
577 | | % quick_bup_core |
578 | | |
579 | | % finding small unsat cores bottom-up by looking for 1, 2, ... conjuncts and looking for contradiction_found |
580 | | % useful e.g. for Z3 where some conjuncts are complex and lead to time outs or translation failed errors |
581 | | % could also be used with WD prover instead or in addition to Z3 |
582 | | % currently cannot be used to find unsat core for timeout or unknown result |
583 | | % useful as a "canary" for PROPERTIES/axioms as a quick check or as additional quick check |
584 | | |
585 | | |
586 | | preprocess_conjuncts([],_,_,[]). |
587 | | preprocess_conjuncts([Conj|T],Nr,Options,ListResult) :- |
588 | | format(' ==> Pre-processing conjunct ~w and checking for individual inconsistency~n',[Nr]), |
589 | | %translate:print_bexpr(Conj),nl, |
590 | | % we could =remove try_prob_solver_first(_) from Options, if we want to filter non-translatable conjuncts! |
591 | | solve_core_pred(Conj, 0.02, Options, Res), |
592 | | (explain_result(Res,Expl) -> debug_format(19,'Result for conjunct ~w: ~w (~w)~n',[Nr,Expl,Res])), |
593 | | N1 is Nr+1, |
594 | | (should_remove_conjunct(Res) |
595 | | -> % remove this conjunct; it can only disturb finding a core for a contradiction |
596 | | preprocess_conjuncts(T,N1,Options,ListResult) |
597 | | ; find_identifier_uses(Conj,[],UsedIds), |
598 | | ListResult = [conj(Nr,Conj,Res,UsedIds)|PT], |
599 | | (contradiction_result(Res,Options) |
600 | | -> PT=[] % stop processing, we have found an unsat core of a single conjunct |
601 | | ; preprocess_conjuncts(T,N1,Options,PT)) |
602 | | ). |
603 | | |
604 | | should_remove_conjunct(no_solution_found(translation_failed)). |
605 | | |
606 | | contradiction_result(contradiction_found,_). |
607 | | contradiction_result(no_solution_found(unfixed_deferred_sets),Options) :- |
608 | | member(allow_unfixed_deferred_sets,Options). |
609 | | |
610 | | quick_bup_core_up_to(Typed,MaxSize,CoreList,Result) :- |
611 | | quick_bup_core(Typed,[use_smt_solver(z3axm), |
612 | | maximum_core_size(MaxSize), |
613 | | try_prob_solver_first(fixed_time_out(5))], |
614 | | CoreList,Result). |
615 | | |
616 | | quick_bup_core(Predicate,Options,CoreList,Result) :- |
617 | | uc_conjunction_to_list(Predicate,Options,List), |
618 | | preprocess_conjuncts(List,1,Options,ProcessedList), |
619 | | (last(ProcessedList,conj(_,Last,LR,_)), |
620 | | contradiction_result(LR,Options) |
621 | | -> CoreList = [Last], Result=LR |
622 | | ; (member(maximum_core_size(MaxN),Options) -> true ; MaxN=3), |
623 | | between(2,MaxN,N), |
624 | | format(' ==> Finding cores of length ~w~n',[N]), |
625 | | contradiction_check(N,ProcessedList,Options,CoreList,Result) -> true |
626 | | ). |
627 | | |
628 | | |
629 | | |
630 | | select_core_target(N,List,Res) :- |
631 | | append(_,[conj(Nr,ConjN,_,UsedIdsN)|Rest],List), % now only use conjuncts to the right of ConjN |
632 | | format(' ==> finding (~w-ary) conflicts with conjunct ~w: ',[N,Nr]),translate:print_bexpr(ConjN),nl, |
633 | | N1 is N-1, |
634 | | select_core_target(N1,Rest,[ConjN],UsedIdsN,Res). |
635 | | |
636 | | has_common_ids(UsedIdsSoFar,conj(_,_,_,UsedIdsN)) :- ord_intersect(UsedIdsSoFar,UsedIdsN). |
637 | | |
638 | | % select a core target list of predicates of length N |
639 | | % TODO: this can still generate the same conjunct multiple times?? |
640 | | select_core_target(0,_,Core,_,Res) :- !, Res=Core. |
641 | | select_core_target(N,List,CoreSoFar,UsedIdsSoFar,Res) :- %print(n(N,UsedIdsSoFar)),nl, |
642 | | split_list(has_common_ids(UsedIdsSoFar),List,CommonList,DisjList), |
643 | | append(_,[conj(_Nr,ConjN,_,UsedIdsN)|Rest],CommonList), % only choose conjuncts with common ids larger than Nr |
644 | | %print(selected_conj(_Nr)),nl, translate:print_bexpr(ConjN),nl, |
645 | | append(Rest,DisjList,RestList), |
646 | | ord_union(UsedIdsN,UsedIdsSoFar,New), |
647 | | N1 is N-1, |
648 | | select_core_target(N1,RestList,[ConjN|CoreSoFar],New,Res). |
649 | | |
650 | | contradiction_check(N,ProcessedList,Options,CoreList,Res) :- |
651 | | select_core_target(N,ProcessedList,CoreList), |
652 | | conjunct_predicates(CoreList,Core), |
653 | | %nl,translate:print_bexpr(Core),nl,nl, |
654 | | solve_core_pred(Core, 0.02, Options, Res), |
655 | | progress_info(Res), |
656 | | contradiction_result(Res,Options). |
657 | | |
658 | | % could use WD prover as additional task: |
659 | | % >>> :prove x:NATURAL --> NATURAL => not(-1:ran(x)) |
660 | | % Universally quantified predicate is PROVED with [prob-wd-prover(no_double_check)] |
661 | | |
662 | | % example where we need three conjuncts: :z3-axm-qcore x<y & y<10 & x<10 & y+n<x & n=0 |