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 | | :- module(enabling_analysis,[reset_enabling_analysis/0, |
6 | | tcltk_cbc_cfg_analysis/1, tcltk_dot_cfg_analysis/1, |
7 | | tcltk_cbc_enabling_analysis/1, tcltk_cbc_enabling_analysis/2, |
8 | | tcltk_cbc_enabling_relations_for_operation/3, |
9 | | tcltk_cbc_enabling_relations_after_operation/3, compute_cbc_enable_rel/4, |
10 | | tcltk_cbc_simultaneous_enabling_analysis/1, |
11 | | tcltk_cbc_dependence_analysis/1, print_enable_table/1, |
12 | | eop_node_predicate/6,init_or_op/1, |
13 | | feasible_operation/2, feasible_operation_with_timeout/3, |
14 | | check_if_feasible_operation/5, |
15 | | feasible_operation_cache/2,infeasible_operation/1, infeasible_operation_cache/1, |
16 | | cbc_enable_analysis/4, |
17 | | is_timeout_enabling_result/1, |
18 | | translate_enable_res/6, |
19 | | operation_sequence_possible/3, operation_can_be_enabled_by/4, |
20 | | partition_predicate/4, |
21 | | cbc_enable_analysis_cache/4 % for prologTasks |
22 | | ]). |
23 | | |
24 | | :- use_module(probsrc(module_information),[module_info/2]). |
25 | | :- module_info(group,cbc). |
26 | | :- module_info(description,'This module computes enabling relations for B operations.'). |
27 | | % -------------- |
28 | | |
29 | | :- use_module(probporsrc(static_analysis), [action_dependent_to_itself/4, get_conj_inv_predicate/3, is_timeout/1]). |
30 | | :- use_module(probsrc(bmachine), [b_top_level_operation/1]). |
31 | | :- use_module(probsrc(b_state_model_check),[get_negated_guard/3,get_negated_guard/4]). |
32 | | :- use_module(library(lists)). |
33 | | :- use_module(probsrc(error_manager)). |
34 | | :- use_module(probsrc(debug)). |
35 | | :- use_module(probsrc(bsyntaxtree), [conjunct_predicates/2]). |
36 | | :- use_module(probsrc(translate), [translate_bexpression/2]). |
37 | | :- use_module(probsrc(b_read_write_info), |
38 | | [b_get_read_write/3,b_get_read_write_vars/5, b_get_operation_read_guard_vars/4]). |
39 | | :- use_module(probsrc(static_enabling_analysis),[static_cannot_enable/2, vars_ord_intersect/2]). |
40 | | :- use_module(cbcsrc(cbc_path_solver), [testcase_path_timeout_catch/9]). |
41 | | :- use_module(probsrc(tools),[ajoin/2,start_ms_timer/1,stop_ms_timer_with_debug_msg/2]). |
42 | | |
43 | | % -------------------- BINARY STATIC ANALYSIS ------------------------- |
44 | | |
45 | | % certain static analyses used by CBC Test case generation |
46 | | |
47 | | % return Res=impossible if from Invariant we can never execute OpName1 followed by OpName2 |
48 | | % is used by sap:check_operation_sequence_possible |
49 | | operation_sequence_possible(OpName1,OpName2,Res) :- |
50 | | static_cannot_enable(OpName1,OpName2), %\+ static_activation_dependent(OpName1,OpName2), |
51 | | !, |
52 | | Res = activation_independent. |
53 | | operation_sequence_possible(OpName1,OpName2,Res) :- |
54 | | % we assume OpName2 is feasible on its own |
55 | | get_negated_guard(OpName2,PosGuard2,_NegGuard2), |
56 | | Timeout1 = 200, |
57 | | % print('Guard: '),translate:print_bexpr(PosGuard2),nl, |
58 | | (testcase_path_timeout_catch(invariant,Timeout1,[OpName1],PosGuard2,_Csts,_Ops,_TestS,_TI,Res) |
59 | | -> true |
60 | | ; Res = impossible |
61 | | ). |
62 | | |
63 | | |
64 | | % return whether OpName1 can enable a previously disabled OpName2 |
65 | | operation_can_be_enabled_by(OpName1,OpName2,TIMEOUT,Res) :- |
66 | | b_top_level_operation(OpName1), |
67 | | b_get_read_write(OpName1,_Reads1,Writes1), |
68 | | b_top_level_operation(OpName2), |
69 | | formatsilent("~nCHECKING if ~w (~w) can enable ~w~n",[OpName1,Writes1,OpName2]), |
70 | | \+ static_cannot_enable(OpName1,OpName2), |
71 | | get_negated_guard(OpName2,PosGuard2,_NegGuard2), |
72 | | filter_predicate(PosGuard2,Writes1,FilteredPosGuard2), |
73 | | create_negation(FilteredPosGuard2,FilteredNegGuard2), |
74 | | % TIMEOUT used to be hardwired to 500 |
75 | | % print('Before: '), translate:print_bexpr(FilteredNegGuard2),nl,print('After: '), translate:print_bexpr(PosGuard2),nl, |
76 | | testcase_path_timeout_catch(pred(FilteredNegGuard2),TIMEOUT,[OpName1],PosGuard2,_,_,_,_,Res), |
77 | | println_silent(can_be_enabled_result(Res)). |
78 | | |
79 | | |
80 | | |
81 | | % -------------------- CFG ANALYSIS ------------------------- |
82 | | :- use_module(dotsrc(dot_graph_generator), [gen_dot_graph/3]). |
83 | | tcltk_dot_cfg_analysis(File) :- |
84 | | gen_dot_graph(File,eop_node_predicate,cfg_trans_predicate). |
85 | | |
86 | | :- use_module(probsrc(preferences),[get_preference/2]). |
87 | | op_node_predicate(NodeID,SubGraph,NodeDesc,Shape,Style,Color) :- |
88 | | get_preference(dot_enabling_show_readwrites,false), !, |
89 | | SubGraph=none, Shape=box, Style=none, |
90 | | b_top_level_operation(Op), NodeID = Op, NodeDesc=Op, Color=blue. |
91 | | op_node_predicate(NodeID,SubGraph,NodeDesc,Shape,Style,Color) :- SubGraph=none, |
92 | | Shape=record, Style=none, |
93 | | b_top_level_operation(Op), NodeID = Op, |
94 | | %NodeDesc = NodeID, |
95 | | b_get_read_write(Op,Reads2,Writes1), |
96 | | b_get_operation_read_guard_vars(Op,false,ReadsGrd,_Precise), |
97 | | insert_commas(ReadsGrd,0,R1), insert_commas(Reads2,0,R2), insert_commas(Writes1,0,W1), |
98 | | append(W1,['}|'],W2), |
99 | | append(R1,['\\n|reads: '|R2],Reads), |
100 | | append(Reads,['\\n|writes: '|W2],As), |
101 | | ajoin(['|{',Op,'\\n|reads (guard): '|As],NodeDesc), |
102 | | Color=blue. |
103 | | |
104 | | insert_commas('$all',_,['ALL']). |
105 | | insert_commas([],_,[]). |
106 | | insert_commas([H],_,R) :- !, R=[H]. |
107 | | insert_commas([H|T],N,[H,Sep|IT]) :- |
108 | | (N>5 -> N1=0, Sep=',\\n' ; N1 is N+1, Sep=', '), |
109 | | insert_commas(T,N1,IT). |
110 | | |
111 | | cfg_trans_predicate(NodeID,Label,SuccID,Color,Style) :- |
112 | | cbc_quick_cfg_analysis(NodeID,SuccID,Res), |
113 | | translate_res(Res,NodeID,SuccID,Label,Color,Style). |
114 | | |
115 | | translate_res(cannot_enable,_,_,_,_,_) :- !, fail. |
116 | | translate_res(syntactic_independent,X,Y,'independent',yellow,bold) :- !,X=Y. % only show if source & dest identical |
117 | | translate_res(race_dependent,X,Y,'race_independent',lightgray,bold) :- !,X=Y. % only show if source & dest identical |
118 | | translate_res(possible,_,_,'',black,solid) :- !. |
119 | | translate_res(timeout_possible,_,_,'',tomato,solid) :- !. |
120 | | translate_res(X,_,_,X,red,dashed). |
121 | | |
122 | | % compute a Control Flow Graph very quickly |
123 | | % ideal when we have program counter style variables |
124 | | tcltk_cbc_cfg_analysis(list([list(['Origin'|SOps])|Result])) :- |
125 | | findall(Op, b_top_level_operation(Op), Ops), sort(Ops,SOps), |
126 | | findall(list([OpName1|EnableList]), |
127 | | (b_get_sorted_op(OpName1), |
128 | | findall(Possible,cbc_quick_cfg_analysis(OpName1,_OpName2,Possible),EnableList)), |
129 | | Result), |
130 | | print_enable_table([list(['Origin'|Ops])|Result]). |
131 | | |
132 | | /* TO DO: complete into a determinacy analysis ? |
133 | | cbc_quick_det_analysis(OpName1,OpName2,Res) :- |
134 | | b_top_level_operation(OpName1), % top_level |
135 | | ((b_get_sorted_op(OpName2), OpName2 \= OpName1, |
136 | | % get_negated_guard(OpName1,PosGuard1,NegGuard1), |
137 | | get_negated_guard(OpName2,PosGuard2,NegGuard2), |
138 | | testcase_path_timeout(pred(PosGuard2),1200,[OpName1],b(truth,pred,[]),_,_,_,_,Res)) |
139 | | -> format(user_output,'Operation ~w can be simultaneously enabled with ~w (~w).~n',[OpName1,OpName2,Res]) |
140 | | ; format(user_output,'Operation ~w cannot be simultanously enabled with another operation.~n',[OpName1]), Res=det |
141 | | ). |
142 | | */ |
143 | | |
144 | | cbc_quick_cfg_analysis(OpName1,OpName2,Res) :- |
145 | | OpName1='INITIALISATION', |
146 | | b_top_level_operation(OpName2), |
147 | | b_get_read_write(OpName2,Reads2,Writes2), |
148 | | formatsilent(user_output,"COMPUTING CFG: INITIALISATION --> ~w r:~w / w:~w~n~n", |
149 | | [OpName2,Reads2,Writes2]), |
150 | | ( testcase_path_timeout_catch(init,250,[OpName2],b(truth,pred,[]),_Constants,_Ops,_TestS,_TI,R1) |
151 | | -> formatsilent(user_output," ~w can be enabled by ~w (~w)!~n",[OpName2,OpName1,R1]), |
152 | | (is_timeout(R1) -> Res=timeout_possible; Res = possible) |
153 | | ; formatsilent(user_output," ~w cannot be enabled by ~w!~n",[OpName2,OpName1]), |
154 | | Res = cannot_enable |
155 | | ). |
156 | | cbc_quick_cfg_analysis(OpName1,OpName2,Res) :- |
157 | | b_top_level_operation(OpName1), % top_level |
158 | | b_get_read_write(OpName1,Reads1,Writes1), |
159 | | %get_negated_guard(OpName1,PosGuard1,NegGuard1), |
160 | | b_get_sorted_op(OpName2), |
161 | | (b_get_read_write(OpName2,Reads2,Writes2), |
162 | | formatsilent(user_output,"COMPUTING CFG: ~w r:~w / w:~w --> ~w r:~w / w:~w~n~n", |
163 | | [OpName1,Reads1,Writes1,OpName2,Reads2,Writes2]), |
164 | | (\+ vars_ord_intersect(Writes1,Reads2) |
165 | | -> (vars_ord_intersect(Writes1,Writes2) |
166 | | -> Res = race_dependent, formatsilent(user_output," ~w cannot be enabled/disabled by ~w!~n",[OpName2,OpName1]) |
167 | | ; Res = syntactic_independent, formatsilent(user_output," ~w cannot be enabled/disabled/modified by ~w!~n",[OpName2,OpName1]) |
168 | | ) |
169 | | ; get_negated_guard(OpName2,PosGuard2,NegGuard2), |
170 | | garbage_collect, |
171 | | print('Guard: '), translate:print_bexpr(PosGuard2),nl, |
172 | | ((testcase_path_timeout_catch(typing(NegGuard2),60,[OpName1],PosGuard2,_,_,_,_,_R0), % quick check without invariants & properties |
173 | | % TO DO: project constants, variables onto the ones really needed |
174 | | testcase_path_timeout_catch(pred(NegGuard2),250,[OpName1],PosGuard2,_Constants,_Ops,_TestS,_TI,R1)) |
175 | | -> formatsilent(user_output," ~w can be enabled by ~w (~w)!~n",[OpName2,OpName1,R1]), |
176 | | (is_timeout(R1) -> Res=timeout_possible; Res = possible) |
177 | | ; formatsilent(user_output," ~w cannot be enabled by ~w!~n",[OpName2,OpName1]), |
178 | | Res = cannot_enable) |
179 | | ) |
180 | | ). |
181 | | |
182 | | %use_module(enabling_analysis),enabling_analysis:cbc_quick_cfg_analysis('SEQUENCER2','SEQUENCER3',Res). |
183 | | %use_module(cbcsrc(sap)),use_module(enabling_analysis),enabling_analysis:cbc_quick_cfg_analysis('COMPUTE_SDDBs_points','prop_COMPUTE_0',Res). |
184 | | %use_module(cbcsrc(sap)),use_module(enabling_analysis),enabling_analysis:cbc_quick_cfg_analysis('COMPUTE_SDDBs_points','COMPUTE_SDDBs_length',Res). |
185 | | |
186 | | % -------------------- DEPENDENCE ANALYSIS ---------------------- |
187 | | /* |
188 | | |
189 | | Independency between two actions in a B/Event-B model can be expressed also by means of LTL-formulas: |
190 | | 1. Two actions a and b are independent if the following LTL-formula is satisfied by the model: |
191 | | "G ((e(b) & [a] => X e(b)) & (e(a) & [b] => X e(a)))" (ind) |
192 | | 2. If (ind) is violated by the model then a and b are dependent actions. |
193 | | |
194 | | Note: The race_dependent condition cannot be covered by the above LTL-formula. If two actions are race dependent, but never simultaniously enabled in a state |
195 | | from the state space, then (ind) will be satisfied. |
196 | | |
197 | | */ |
198 | | |
199 | | tcltk_cbc_dependence_analysis(list([list(['Origin'|SOps])|Result])) :- |
200 | | %findall(Op, b_get_machine_operation(Op,_,_,_), Ops), sort(Ops,SOps), |
201 | | findall(Op, b_top_level_operation(Op), Ops), sort(Ops,SOps), |
202 | | findall(list([OpName1|EnableList]), |
203 | | (b_get_sorted_op(OpName1), |
204 | | findall(Enable,cbc_dependence_analysis(OpName1,_OpName2,Enable),EnableList)), |
205 | | Result). |
206 | | %,print_enable_table([list(['Origin'|Ops])|Result]). |
207 | | |
208 | | b_get_sorted_op(Op) :- findall(Op, b_top_level_operation(Op), Ops), |
209 | | sort(Ops,SOps), member(Op,SOps). |
210 | | |
211 | | cbc_dependence_analysis(OpName1,OpName2,Res) :- |
212 | | b_top_level_operation(OpName1), |
213 | | b_get_read_write_vars(OpName1,GReads1,AReads1,Reads1,Writes1), |
214 | | b_get_sorted_op(OpName2), |
215 | | (OpName1=OpName2 -> action_dependent_to_itself(OpName1,GReads1,Writes1,Res) % TO DO: check if it is possible that for two different parameter values of the same event there is dependence |
216 | | ; OpName2 @< OpName1 -> Res = '-' % our checking is symmetric; only check one pair |
217 | | ; |
218 | | b_get_read_write_vars(OpName2,GReads2,AReads2,Reads2,Writes2), |
219 | | formatsilent("CHECKING DEPENDENCE: ~w gr:~w / ar:~w / w:~w <--> ~w gr:~w / ar:~w / w:~w~n",[OpName1,GReads1,AReads1,Writes1,OpName2,GReads2,AReads2,Writes2]), |
220 | | ( syntactical_independence(Reads1,Writes1,Reads2,Writes2) -> Res = syntactic_independent |
221 | | ; vars_ord_intersect(Writes1,Writes2) -> Res = race_dependent |
222 | | ; (vars_ord_intersect(AReads1,Writes2);vars_ord_intersect(AReads2,Writes1)) -> Res = race_dependent |
223 | | % TO DO: in this case check if there is indeed a race dependence (e.g. in scheduler new and ready_active are actually independent !) |
224 | | % Set up [OpName1,OpName2], [OpName2,OpName1] and see if for ord_intersect(Writes1,Writes2) we can find different values |
225 | | ; |
226 | | % get constraints for G_{Op_1} \leadsto{Op_2} not(G_{Op_1}) |
227 | | get_negated_guard(OpName1,PosGuard1,NegGuard1), |
228 | | get_conj_inv_predicate([NegGuard1],1,NegGuard1_Inv), |
229 | | % get constraints for G_{Op_2} \leadsto{Op_1} not(G_{Op_2}) |
230 | | get_negated_guard(OpName2,PosGuard2,NegGuard2), |
231 | | get_conj_inv_predicate([NegGuard2],1,NegGuard2_Inv), |
232 | | conjunct_predicates([PosGuard1,PosGuard2],GuardsConj), |
233 | | (vars_ord_intersect(GReads1,Writes2), |
234 | | testcase_path_timeout_catch(pred(GuardsConj),500,[OpName2],NegGuard1_Inv,_Constants,_Ops,_TestS,_TI,R1) |
235 | | -> formatsilent(" ~w may disable ~w (~w)!~n",[OpName2,OpName1,R1]), |
236 | | (is_timeout(R1) -> Res=timeout_dependent; Res = dependent) |
237 | | ; (vars_ord_intersect(GReads2,Writes1), |
238 | | testcase_path_timeout_catch(pred(GuardsConj),500,[OpName1],NegGuard2_Inv,_,_,_,_,R2)) |
239 | | -> formatsilent(" ~w may disable ~w (~w)!~n",[OpName1,OpName2,R2]), |
240 | | (is_timeout(R2) -> Res=timeout_dependent; Res = dependent) |
241 | | ; Res = independent))). |
242 | | |
243 | | syntactical_independence(Reads1,Writes1,Reads2,Writes2) :- |
244 | | \+ vars_ord_intersect(Writes1,Reads2), % op1 does not modify guard/effect of op2 |
245 | | \+ vars_ord_intersect(Writes2,Reads1), % op2 does not modify guard/effect of op1 |
246 | | \+ vars_ord_intersect(Writes1,Writes2). % no race condition |
247 | | |
248 | | % -------------- ENABLING ANALYSIS -------------- |
249 | | |
250 | | :- dynamic cbc_enable_analysis_cache/4. |
251 | | |
252 | | % check which operations can be enabled after executing another operation |
253 | | % to do: move maybe to another module; provide proper CSV export using Sebastian's modules |
254 | | cbc_enable_analysis(OpName1,OpName2,Enable,ExtraTimeout) :- |
255 | ? | init_or_op(OpName1), |
256 | | if(cbc_enable_analysis_cache(OpName1,OpName2,Result,ExtraTimeout), |
257 | | Result = Enable, % already computed |
258 | | (% now compute all enablings form OpName1 |
259 | ? | cbc_enable_analysis_calc(OpName1,Op2,R,ExtraTimeout), |
260 | | assertz(cbc_enable_analysis_cache(OpName1,Op2,R,ExtraTimeout)), |
261 | | fail |
262 | | ; |
263 | | % now look up: |
264 | ? | cbc_enable_analysis_cache(OpName1,OpName2,Enable,ExtraTimeout) |
265 | | )). |
266 | | |
267 | | |
268 | | cbc_enable_analysis_calc(OpName1,OpName2,Enable,ExtraTimeout) :- |
269 | | OpName1='INITIALISATION',printsilent('CHECKING ENABLING AFTER INITIALISATION'),nls, |
270 | ? | b_top_level_operation(OpName2), |
271 | | printsilent('INITIALISATION'), printsilent(' ---> '), printsilent(OpName2), printsilent(' :: '), |
272 | | add_to_time_out(ExtraTimeout,200,Timeout), |
273 | | ( testcase_path_timeout_catch(init,Timeout,[OpName2],b(truth,pred,[]),_Constants,_Ops,_TestS,_TI,R) |
274 | | -> printsilent(R), printsilent(' : '), |
275 | | get_negated_guard(OpName2,_,NegGuard), |
276 | | (testcase_path_timeout_catch(init,Timeout,[],NegGuard,_Constants2,_Ops2,_TestS2,_TI2,R2) |
277 | | -> printsilent(R2), printsilent(' : '),Enable=possible |
278 | | ; Enable=guaranteed) |
279 | | ; Enable=impossible), |
280 | | printsilent(Enable),nls. |
281 | | cbc_enable_analysis_calc(OpName1,OpName2,Enable,ExtraTimeout) :- |
282 | | b_top_level_operation(OpName1), |
283 | | b_get_read_write(OpName1,Reads1,Writes1), |
284 | | %b_get_operation_read_guard_vars(OpName1,true,Reads1,_Precise), |
285 | | %get_negated_guard(OpName1,PosGuard1,_NegGuard1), |
286 | | formatsilent("CHECKING ENABLING AFTER: ~w r:~w / w:~w~n",[OpName1,Reads1,Writes1]), |
287 | ? | b_top_level_operation(OpName2), |
288 | | formatsilent('~w ---> ~w :: ',[OpName1,OpName2]), |
289 | | cbc_enable_calc_aux(OpName1,Writes1,OpName2,Enable,ExtraTimeout), |
290 | | formatsilent('Enable=~w~n',[Enable]). |
291 | | |
292 | | :- use_module(probporsrc(static_analysis),[syntactic_independence/3]). |
293 | | cbc_enable_calc_aux(OpName1,_Writes1,_OpName2,Enable,ExtraTimeout) :- |
294 | | (ExtraTimeout>500 -> infeasible_operation(OpName1) |
295 | | ; infeasible_operation_cache(OpName1)), % only check cached version |
296 | | !, |
297 | | Enable=infeasible. |
298 | | cbc_enable_calc_aux(_OpName1,_Writes1,OpName2,Enable,ExtraTimeout) :- |
299 | | (ExtraTimeout>500 -> infeasible_operation(OpName2) |
300 | | ; infeasible_operation_cache(OpName2)), % only check cached version |
301 | | !, |
302 | | Enable=impossible_infeasible. |
303 | | cbc_enable_calc_aux(OpName1,_Writes1,OpName2,Enable,_) :- |
304 | | % first check if we can syntactically determine independence |
305 | | syntactic_independence(OpName1,OpName2,Res), |
306 | | !, |
307 | | Enable=Res. |
308 | | cbc_enable_calc_aux(OpName1,Writes1,OpName2,Enable,ExtraTimeout) :- |
309 | | % now we do the semantic checks |
310 | | add_to_time_out(ExtraTimeout,200,Timeout1), |
311 | | add_to_time_out(ExtraTimeout,300,Timeout2), |
312 | | |
313 | | get_negated_guard(OpName2,PosGuard2,NegGuard2), |
314 | | filter_predicate(PosGuard2,Writes1,FilteredPosGuard2), |
315 | | create_negation(FilteredPosGuard2,FilteredNegGuard2), |
316 | | cbc_enable_calc_aux2(OpName1,OpName2,Enable,Timeout1,Timeout2,PosGuard2,NegGuard2,FilteredNegGuard2). |
317 | | % TO DO: return timeout results and compute timeout info here |
318 | | |
319 | | |
320 | | cbc_enable_calc_aux2(OpName1,OpName2,Enable,Timeout1,Timeout2,PosGuard2,NegGuard2,FilteredNegGuard2) :- |
321 | | %format('Check if ~w can be enabled after ~w ~n',[OpName2,OpName1]), |
322 | | %print(' Pos Guard: '),translate:print_bexpr(PosGuard2),nl, |
323 | | %((OpName1=winc,OpName2=winc) -> trace ; true), |
324 | | testcase_path_timeout_catch(invariant,Timeout1,[OpName1],PosGuard2,_Csts,_Ops,_TestS,_TI,R), % advantage over version with [OpName1,OpName2] : one less state to setup and enumerate; but inner guards may not be checked |
325 | | !, |
326 | | printsilent(can_be_enabled_after(R)), printsilent(' : '), |
327 | | % print('Neg Guard: '),translate:print_bexpr(NegGuard2),nl, |
328 | | % TO DO: first check whether OpName2 can be disabled given Invariant ? |
329 | | (testcase_path_timeout_catch(invariant,Timeout1,[OpName1],NegGuard2,_Csts2,_Ops2,_TestS2,_TI2,R2) |
330 | | -> printsilent(can_be_disabled_after(R2)), printsilent(' : '), |
331 | | % TO DO: test if NegGuard2 holds initially it is possible to execute [OpName1,OpName2]; if not: OpName1 cannot enable OpName2, only keep it -> Enable=keep_possible |
332 | | % then we could check if OpName1 can disable OpName2: PosGuard2,[OpName1],NegGuard2 |
333 | | (OpName1\=OpName2, % otherwise OpName2 must be enabled if [OpName1] can be executed |
334 | | %testcase_path_timeout_catch(pred(NegGuard2),Timeout2,[OpName1,OpName2],b(truth,pred,[]),_,_,_,_,R3) |
335 | | testcase_path_timeout_catch(pred(NegGuard2),Timeout2,[OpName1],PosGuard2,_,_,_,_,R3) |
336 | | -> printsilent(can_enable(R3)), printsilent(' : '), |
337 | | %nl, translate:print_bexpr(PosGuard2),nl,print(OpName1),nl,translate:print_bexpr(NegGuard2),nl, |
338 | | %print('FILTERED: '), translate:print_bexpr(FilteredNegGuard2),nl, |
339 | | (testcase_path_timeout_catch(pred(PosGuard2),Timeout2,[OpName1],FilteredNegGuard2,_,_,_,_,R4) |
340 | | -> printsilent(can_disable(R4)), printsilent(' : '), |
341 | | (contains_timeout([R,R2,R3,R4]) -> Enable=timeout_possible ; Enable=possible) |
342 | | ; printsilent('cannot_disable : '), |
343 | | (contains_timeout([R,R2,R3]) -> Enable=timeout_possible_enable ; Enable=possible_enable) /* Opname cannot disable OpName2; only enable it */ |
344 | | ) |
345 | | ; /* OpName1 cannot enable OpName2; only preserve it */ |
346 | | printsilent('cannot_enable : '), |
347 | | (testcase_path_timeout_catch(pred(PosGuard2),Timeout2,[OpName1],FilteredNegGuard2,_,_,_,_,R4) |
348 | | -> printsilent(can_disable(R4)), printsilent(' : '), |
349 | | (contains_timeout([R,R2,R4]) -> Enable=timeout_possible_disable; Enable=possible_disable) |
350 | | ; (contains_timeout([R,R2]) -> Enable=timeout_keep; Enable=keep) /* Opname cannot enable or disable */ |
351 | | ) |
352 | | ) |
353 | | ; (is_timeout(R) -> Enable=timeout_guaranteed; Enable=guaranteed) |
354 | | ). |
355 | | cbc_enable_calc_aux2(OpName1,_OpName2,Enable,_Timeout1,Timeout2,PosGuard2,_NegGuard2,FilteredNegGuard2) :- |
356 | | % OpName2 can never be enabled after OpName1; check whether it can be enabled before |
357 | | % Note: we could replace FilteredNegGuard2 by truth |
358 | | testcase_path_timeout_catch(pred(PosGuard2),Timeout2,[OpName1],FilteredNegGuard2,_,_,_,_,R4), |
359 | | !, |
360 | | printsilent(can_disable(R4)), printsilent(' : '), |
361 | | (is_timeout(R4) -> Enable=timeout_impossible; Enable=impossible). |
362 | | cbc_enable_calc_aux2(_OpName1,_OpName2,Enable,_,_,_PosGuard2,_NegGuard2,_FilteredNegGuard2) :- |
363 | | % OpName2 can never ben enabled after nor before |
364 | | Enable=impossible_keep. |
365 | | |
366 | | |
367 | | % check if contains timeout or similar result |
368 | ? | contains_timeout(List) :- (member(timeout,List) -> true ; member(clpfd_overflow,List) -> true). |
369 | | |
370 | | |
371 | | :- use_module(probsrc(bsyntaxtree),[conjunction_to_list/2,some_id_occurs_in_expr/2,create_negation/2]). |
372 | | % remove all predicates which are not modified |
373 | | filter_predicate(Pred,ModifiedVars,FilteredPred) :- partition_predicate(Pred,ModifiedVars,FilteredPred,_). |
374 | | |
375 | | % partition all predicates into those which are not modified and those that can be |
376 | | partition_predicate(Pred,Var,FilteredPred,Rest) :- var(Var),!, |
377 | | add_internal_error('Missing write variables:',partition_predicate(Pred,Var,FilteredPred,Rest)), |
378 | | partition_predicate(Pred,'$all',FilteredPred,Rest). |
379 | | partition_predicate(Pred,'$all',FilteredPred,Rest) :- !, FilteredPred=Pred, Rest=b(truth,pred,[]). |
380 | | partition_predicate(Pred,ModifiedVars,FilteredPred,Rest) :- |
381 | | conjunction_to_list(Pred,List), |
382 | | partition(can_be_modified(ModifiedVars),List,FilteredList,[],UnFilteredList), |
383 | | conjunct_predicates(FilteredList,FilteredPred), |
384 | | conjunct_predicates(UnFilteredList,Rest). |
385 | | |
386 | | can_be_modified(ModifiedVars,Pred,Res) :- |
387 | | some_id_occurs_in_expr(ModifiedVars,Pred),!, Res='<'. |
388 | | can_be_modified(_,_,'>'). |
389 | | |
390 | | %predicate_modified_by_write_vars(_Pred,'$all') :- !. |
391 | | %predicate_modified_by_write_vars(Pred,SortedWriteVars) :- some_id_occurs_in_expr(SortedWriteVars,Pred). |
392 | | |
393 | | |
394 | | :- use_module(probsrc(preferences),[get_time_out_preference_with_factor/2, add_time_outs/3]). |
395 | | |
396 | | add_to_time_out(Extra,OriginalTimeout,New) :- |
397 | | (OriginalTimeout < 300 % default for timeout_cbc_analysis is 300 |
398 | | -> E is (Extra * OriginalTimeout) // 300 |
399 | | ; E is Extra |
400 | | ), |
401 | | add_time_outs(OriginalTimeout,E,New). |
402 | | |
403 | | tcltk_cbc_enabling_analysis(L) :- |
404 | | get_time_out_preference_with_factor(0.1,TOF), |
405 | | (TOF > 250 -> ExtraTO is TOF-250 ; ExtraTO = 0), |
406 | | tcltk_cbc_enabling_analysis(ExtraTO,L). |
407 | | tcltk_cbc_enabling_analysis(ExtraTimeout,list([list(['Origin'|Ops])|Result])) :- |
408 | | findall(Op, b_top_level_operation(Op), Ops), |
409 | | %statistics(walltime,[WT1,_]), |
410 | | findall(list([OpName1|EnableList]), |
411 | | (init_or_op(OpName1), |
412 | | findall(Enable,cbc_enable_analysis(OpName1,_OpName2,Enable,ExtraTimeout),EnableList)), |
413 | | Result). |
414 | | %statistics(walltime,[WT2,_]), Time is WT2-WT1, |
415 | | %format('Runtime for enabling analysis: ~w ms~n',[Time]) |
416 | | %,print_enable_table([list(['Origin'|Ops])|Result]). |
417 | | |
418 | | % ----------------- |
419 | | % compute the four edges of the enable relation for a particular operation |
420 | | tcltk_cbc_enabling_relations_for_operation(OpName2,ExtraTimeout, |
421 | | list([list(['Event','Enable','KeepEnabled','Disable','KeepDisabled'])|Results])) :- |
422 | | findall(list([OpName1|ResOp2]), |
423 | | compute_cbc_enable_rel(OpName1,OpName2,ExtraTimeout,ResOp2), Results). |
424 | | tcltk_cbc_enabling_relations_after_operation(OpName1,ExtraTimeout, |
425 | | list([list(['Event','Enable','KeepEnabled','Disable','KeepDisabled'])|Results])) :- |
426 | | findall(list([OpName2|ResOp2]), |
427 | | compute_cbc_enable_rel(OpName1,OpName2,ExtraTimeout,ResOp2), Results). |
428 | | |
429 | | % compute the four edges of the enable relation for a pair of events: |
430 | | compute_cbc_enable_rel(OpName1,OpName2,ExtraTimeout,[Enable,KeepEnabled,Disable,KeepDisabled]) :- |
431 | | b_top_level_operation(OpName1), |
432 | | b_get_read_write(OpName1,_,Writes1), |
433 | | b_get_operation_read_guard_vars(OpName1,true,Reads1,_Precise), |
434 | | formatsilent("CHECKING ENABLING RELATION AFTER: ~w r:~w / w:~w~n",[OpName1,Reads1,Writes1]), |
435 | | b_top_level_operation(OpName2), |
436 | | get_negated_guard(OpName2,PosGuard2,_NegGuard2), |
437 | | partition_predicate(PosGuard2,Writes1,FilteredPosGuard2,StaticPosGuard2), |
438 | | create_negation(FilteredPosGuard2,FilteredNegGuard2), |
439 | | Timeout1 is 300+ExtraTimeout, |
440 | | formatsilent('~w ---> ~w :: ',[OpName1,OpName2]), |
441 | | %print(' FPOS: '),translate:print_bexpr(FilteredPosGuard2),nl, |
442 | | %print('Neg Guard: '),translate:print_bexpr(NegGuard2),nl, |
443 | | %print(' Filtered: '),translate:print_bexpr(FilteredNegGuard2),nl, |
444 | | % TO DO: use syntactic conditions |
445 | | my_testcase_path_timeout(pred(PosGuard2),Timeout1,[OpName1],FilteredPosGuard2,KeepEnabled), |
446 | | my_testcase_path_timeout(pred(PosGuard2),Timeout1,[OpName1],FilteredNegGuard2,Disable), |
447 | | (OpName1==OpName2 -> KeepDisabled=false, Enable=false |
448 | | ; conjunct_predicates([StaticPosGuard2,FilteredNegGuard2],NegEnableGuard2), % as StaticPosGuard2 must be same before after: it must be true before to be true after |
449 | | my_testcase_path_timeout(pred(NegEnableGuard2),Timeout1,[OpName1],FilteredPosGuard2,Enable), |
450 | | % this is the unoptimised call: |
451 | | %my_testcase_path_timeout(pred(NegGuard2),Timeout1,[OpName1],NegGuard2,KeepDisabled), |
452 | | % we do two calls for KeepDisabled: pred(NegStaticGuard2) true after, or StaticGuard2 & NegFiltered -> NegFiltered |
453 | | create_negation(StaticPosGuard2,StaticNegGuard2), |
454 | | my_testcase_path_timeout(pred(StaticNegGuard2),Timeout1,[OpName1],b(truth,pred,[]),KeepDisabled1), |
455 | | (KeepDisabled1 = ok -> KeepDisabled=ok |
456 | | ; my_testcase_path_timeout(pred(NegEnableGuard2),Timeout1,[OpName1],FilteredNegGuard2,KeepDisabled) |
457 | | ), |
458 | | formatsilent(' KeepEnabled=~w, Disable=~w, Enable=~w, KeepDisabled=~w~n', |
459 | | [KeepEnabled,Disable,Enable,KeepDisabled]) |
460 | | ). |
461 | | |
462 | | my_testcase_path_timeout(Before,Timeout,Path,After,Result) :- |
463 | | (testcase_path_timeout_catch(Before,Timeout,Path,After,_,_,_,_,Result) -> true |
464 | | ; Result = false). |
465 | | |
466 | | % ---------------------------------------------- |
467 | | |
468 | | |
469 | | init_or_op('INITIALISATION'). |
470 | ? | init_or_op(OpName1) :- b_top_level_operation(OpName1). %b_get_machine_operation(OpName1,_,_,_). |
471 | | |
472 | | % print table in CSV format |
473 | | print_enable_table(R) :- print_enable_table_list(R). |
474 | | print_enable_table_list([]) :- nl,nl. |
475 | | print_enable_table_list([list(L)|T]) :- |
476 | | print_csv_list(L), print_enable_table_list(T). |
477 | | print_csv_list([]) :- !,nl. |
478 | | print_csv_list([H]) :- !,print(H),nl. |
479 | | print_csv_list([H|T]) :- !,print(H), print(','), print_csv_list2(T). |
480 | | print_csv_list(X) :- print(illegal_list(X)),nl, add_internal_error('Illegal list: ', print_csv_list(X)). |
481 | | |
482 | | print_csv_list2([H]) :- !,print_enable_info(H),nl. |
483 | | print_csv_list2([H|T]) :- !,print_enable_info(H), print(','), print_csv_list2(T). |
484 | | print_csv_list2(X) :- print(illegal_list(X)),nl, add_internal_error('Illegal list: ', print_csv_list(X)). |
485 | | |
486 | | print_enable_info(I) :- (translate_enable_info(I,TI) -> print(TI) ; print_for_csv(I)). |
487 | | % simplify result further for output to CSV; reason: also testing (e.g., test 1360 which sometimes causes overflow on Mac, and time_out on Linux) |
488 | | translate_enable_info(timeout,unknown). |
489 | | translate_enable_info(time_out,unknown). |
490 | | translate_enable_info(overflow,unknown). |
491 | | translate_enable_info(virtual_time_out,unknown). |
492 | | |
493 | | print_for_csv([]) :- !, print('{}'). |
494 | | print_for_csv([H|T]) :- !, % print list without commas , |
495 | | format('{~w',[H]), maplist(print_csv_el,T), print('}'). |
496 | | print_for_csv(Term) :- print(Term). |
497 | | print_csv_el(H) :- format(' ~w',[H]). |
498 | | |
499 | | |
500 | | % ---------- create enable graph ------------------ |
501 | | |
502 | | eop_node_predicate(NodeID,SubGraph,NodeDesc,Shape,Style,black) :- |
503 | | op_node_predicate(NodeID,SubGraph,NodeDesc,Shape,Style,_Color). |
504 | | eop_node_predicate('INITIALISATION',SubGraph,'INITIALISATION',Shape,Style,Color) :- SubGraph=none, |
505 | | Shape=hexagon, Style=none, Color=olivedrab2. |
506 | | |
507 | | translate_enable_res(Rel,_,_,'impossible',red,_) :- impossible(Rel),!, fail. |
508 | | translate_enable_res(guaranteed,_,_,'guaranteed',olivedrab2,solid) :- !. |
509 | | translate_enable_res(Rel,X,Y,TransRel,TCol,bold) :- independent(Rel,Col), |
510 | | !,X=Y, % only show if source & dest identical |
511 | | TransRel=Rel,TCol=Col. |
512 | | translate_enable_res(possible,_,_,'possible',black,solid) :- !. |
513 | | translate_enable_res(keep,_,_,'keep',lightgray,solid) :- !. |
514 | | translate_enable_res(possible_enable,_,_,'(enable)',black,solid) :- !. |
515 | | translate_enable_res(possible_disable,_,_,'(disable)',lightblue,solid) :- !. |
516 | | translate_enable_res(timeout_keep,_,_,'keep?',tomato,dashed) :- !. |
517 | | translate_enable_res(timeout_possible,_,_,'possible?',tomato,dashed) :- !. |
518 | | translate_enable_res(timeout_possible_disable,_,_,'(disable?)',tomato,dashed) :- !. |
519 | | translate_enable_res(timeout_possible_enable,_,_,'(enable?)',tomato,dashed) :- !. |
520 | | translate_enable_res(pred(Expr),_,_,String,black,dashed) :- !, |
521 | | translate_bexpression(Expr,String). |
522 | | translate_enable_res(predicate(Expr),_,_,String,black,dashed) :- !, |
523 | | translate_bexpression(Expr,String). |
524 | | translate_enable_res(dependent,_,_,dependent,green,solid) :- !. |
525 | | translate_enable_res(race_dependent,_,_,race_dependent,green,solid) :- !. |
526 | | translate_enable_res(guard_dependent,_,_,guard_dependent,olive,dashed) :- !. |
527 | | translate_enable_res(action_dependent,_,_,action_dependent,green,dashed) :- !. |
528 | | translate_enable_res(X,_,_,'independent',lightgray,solid) :- |
529 | | memberchk(X,[independent,syntactic_independent,syntactic_fully_independent,syntactic_unchanged]),!. |
530 | | translate_enable_res(X,_,_,X,red,solid). |
531 | | |
532 | | |
533 | | impossible(impossible). |
534 | | impossible(impossible_keep). |
535 | | impossible(impossible_disable). |
536 | | impossible(impossible_infeasible). |
537 | | impossible(infeasible). |
538 | | |
539 | | independent(independent,yellow). |
540 | | independent(syntactic_keep,lightgray). |
541 | | independent(syntactic_independent,lightgray). |
542 | | independent(syntactic_fully_independent,lightgray). |
543 | | independent(syntactic_unchanged,lightgray). |
544 | | independent(activation_indepdendent,lightgray). |
545 | | |
546 | | is_timeout_enabling_result(timeout_keep). |
547 | | is_timeout_enabling_result(timeout_possible). |
548 | | is_timeout_enabling_result(timeout_possible_disable). |
549 | | is_timeout_enabling_result(timeout_possible_enable). |
550 | | is_timeout_enabling_result(timeout). |
551 | | is_timeout_enabling_result(timeout_dependent). |
552 | | is_timeout_enabling_result(timeout_guaranteed). |
553 | | is_timeout_enabling_result(timeout_impossible). |
554 | | |
555 | | % Simultaneous Enabling Analysis |
556 | | % Find events which cannot be enabled simultaneously |
557 | | % Usage: |
558 | | % a) if one event is known to be enabled we do not need to check the impossible ones |
559 | | % b) if one event is known to be disabled we do not need check events which guarantee it |
560 | | |
561 | | % use_module(enabling_analysis), tcltk_cbc_simultaneous_enabling_analysis(R). |
562 | | |
563 | | tcltk_cbc_simultaneous_enabling_analysis(list([list(['Origin'|SOps])|Result])) :- |
564 | | findall(Op, b_top_level_operation(Op), Ops), |
565 | | sort(Ops,SOps), % sort operations to ensure nice triangular display |
566 | | statistics(walltime,[WT1,_]), |
567 | | findall(list([OpName1|EnableList]), |
568 | | (member(OpName1,SOps), |
569 | | findall(SimulRes,tcltk_simult(OpName1,SOps,SimulRes),EnableList)), |
570 | | Result), |
571 | | statistics(walltime,[WT2,_]), Time is WT2-WT1, |
572 | | formatsilent('Runtime for simultaneous enabling analysis: ~w ms~n',[Time]) |
573 | | . %,print_enable_table([list(['Origin'|Ops])|Result]). |
574 | | |
575 | | tcltk_simult(Op1,SOps,Res) :- member(Op2,SOps),tcltk_simult2(Op1,Op2,Res). |
576 | | tcltk_simult2(Op1,Op2,Res) :- |
577 | | Op1 = Op2, % Op2 @=< Op1, % the simultaneous impossible analysis is symmetric: TO DO: do not compute/display info twice |
578 | | !, |
579 | | Res = '-'. |
580 | | tcltk_simult2(Op1,Op2,TRes) :- |
581 | | simult_enable_analysis(Op1,Op2,1,fixed_time_out(150),TRes). |
582 | | |
583 | | simult_enable_analysis(OpName1,OpName2,UseInvariant,TimeoutFactor,Res) :- |
584 | | b_top_level_operation(OpName1), |
585 | | b_top_level_operation(OpName2), |
586 | | %OpName2 @>OpName1, |
587 | | get_negated_guard(OpName1,PosGuard1,_NegGuard1), |
588 | | get_negated_guard(OpName2,PosGuard2,NegGuard2), |
589 | | get_conj_inv_predicate([PosGuard1,PosGuard2],UseInvariant,Pred), |
590 | | %print(checking_simultaneous(OpName1,OpName2)),nl, |
591 | | (solve_predicate_with_chr(Pred,_State,TimeoutFactor,[],RR) |
592 | | -> %print(state(_State)),nl, print(result(RR)),nl, |
593 | | ((RR = contradiction_found ; RR = no_solution_found(unfixed_deferred_sets)) |
594 | | -> Res = impossible % we do not compute PosGuard1,NegGuard2 in this case: otherwise this would mean that Operation 1 can never fire ! |
595 | | ; get_conj_inv_predicate([PosGuard1,NegGuard2],UseInvariant,Pred2), |
596 | | solve_predicate_with_chr(Pred2,_State2,TimeoutFactor,[],RR2), |
597 | | combine_simult_result(RR,RR2,Res)) |
598 | | ; print('********* ERROR ***********'),nl,fail). |
599 | | |
600 | | combine_simult_result(solution(_),solution(_),R) :- !,R=possible. |
601 | | combine_simult_result(solution(_),B,R) :- |
602 | | (B = contradiction_found ; B = no_solution_found(unfixed_deferred_sets)), !, |
603 | | R=guaranteed. |
604 | | combine_simult_result(A,B,Res) :- functor(A,FA,_), functor(B,FB,_), |
605 | | ajoin([FA,'_',FB],Res). |
606 | | |
607 | | % --------------------------------------------------- |
608 | | |
609 | | :- use_module(probsrc(eventhandling),[register_event_listener/3]). |
610 | | :- register_event_listener(specification_initialised,reset_enabling_analysis, |
611 | | 'Initialise module enabling analysis.'). |
612 | | |
613 | | :- dynamic feasible_operation_cache/2. |
614 | | reset_enabling_analysis :- |
615 | | %retractall(disable_graph(_)), |
616 | | retractall(feasible_operation_cache(_,_)), |
617 | | retractall(cbc_enable_analysis_cache(_,_,_,_)). |
618 | | |
619 | | % in principle you should only call this for Op with operation_name_not_yet_covered(Op) |
620 | | feasible_operation(Op,Res) :- feasible_operation_cache(Op,CR),!,Res=CR. |
621 | | feasible_operation(Op,Res) :- UseInvariant=1, |
622 | | check_if_feasible_operation(Op,UseInvariant,fixed_time_out(500),CR,_), |
623 | | assertz(feasible_operation_cache(Op,CR)), |
624 | | Res=CR. |
625 | | |
626 | | feasible_operation_with_timeout(Op,TimeOut,Res) :- feasible_operation_cache(Op,CR), |
627 | | (CR \= unknown ; TimeOut =< 500), !, Res=CR. |
628 | ? | feasible_operation_with_timeout(Op,TimeOut,Res) :- check_if_feasible_operation(Op,1,fixed_time_out(TimeOut),CR,_), |
629 | | retractall(feasible_operation_cache(Op,_)), % must be unknown if it was asserted before |
630 | | assertz(feasible_operation_cache(Op,CR)), |
631 | | Res=CR. |
632 | | |
633 | | |
634 | | :- use_module(probsrc(state_space),[operation_not_yet_covered/1]). |
635 | | infeasible_operation(Op) :- feasible_operation_cache(Op,CR),!, CR=impossible. |
636 | | infeasible_operation(Op) :- operation_not_yet_covered(Op), |
637 | | feasible_operation(Op,impossible). % will also assert feasible_operation_cache |
638 | | |
639 | | infeasible_operation_cache(Op) :- feasible_operation_cache(Op,impossible). |
640 | | |
641 | | :- use_module(probsrc(solver_interface), [solve_predicate/5]). |
642 | | solve_predicate_with_chr(Pred,State,TimeoutFactor,Options,RR) :- |
643 | | solve_predicate(Pred,State,TimeoutFactor,['CHR','CLPFD','SMT'|Options],RR). |
644 | | |
645 | | :- use_module(probsrc(debug), [debug_mode/1]). |
646 | | :- use_module(probsrc(store),[normalise_store/2]). |
647 | | % check if an operation is feasible |
648 | | % UseInvariant = 1 : assume invariant |
649 | | check_if_feasible_operation(OpName1,UseInvariant,TimeoutFactor,Res,NormalisedResultState) :- |
650 | ? | b_top_level_operation(OpName1), |
651 | | (debug_mode(on) |
652 | | -> print(checking_feasibility(OpName1)),nl,tools:start_ms_timer(TIMER) ; true), |
653 | ? | if(check_if_feasible_operation2(OpName1,UseInvariant,TimeoutFactor,Res,State),true, |
654 | | (add_internal_error('Failed:',check_if_feasible_operation2(OpName1,UseInvariant,TimeoutFactor,Res,State)),fail)), |
655 | | % TO DO: add missing variables and put into order |
656 | | normalise_enabling_store(State,NormalisedResultState), |
657 | | %b_interpreter:sort_variable_binding(NormalisedResultState,SortedStore), |
658 | | stop_ms_timer_with_debug_msg(TIMER,feasible(OpName1)). |
659 | | |
660 | | normalise_enabling_store('$UNKNOWN_STATE',R) :- !, R='$UNKNOWN_STATE'. |
661 | | normalise_enabling_store(CState,State) :- normalise_store(CState,State). |
662 | | |
663 | | :- use_module(probsrc(tools_timeout),[get_time_out_with_factor/2]). |
664 | | check_if_feasible_operation2(OpName1,UseInvariant,TimeoutFactor,Res,ResultState) :- |
665 | | get_negated_guard(OpName1,PosGuard1,_NegGuard1,Precise), |
666 | | % format('Guard is ~w for ~w~n',[Precise,OpName1]), |
667 | | Precise = precise, |
668 | | !, |
669 | | % Warning: guard is under approximation for sequential composition, while loops, ... |
670 | | get_conj_inv_predicate([PosGuard1],UseInvariant,Pred), |
671 | | (debug_mode(on) -> translate:nested_print_bexpr(Pred),nl ; true), |
672 | | % visualize_graph:print_predicate_dependency_as_graph_for_dot(Pred,'~/Desktop/out.dot'), |
673 | | (solve_predicate_with_chr(Pred,State,TimeoutFactor,[full_machine_state],RR) |
674 | | -> %print(state(_State)),nl, print(result(RR)),nl, |
675 | | ((RR=no_solution_found(unfixed_deferred_sets) ; RR = contradiction_found) |
676 | | -> Res = impossible, ResultState = '$UNKNOWN_STATE' |
677 | | %,format('Computing unsat core for infeasible ~w~n',[OpName1]),unsat_cores:unsat_core(Pred,no_solution_found,Core),print('CORE: '),nl,translate:nested_print_bexpr_as_classicalb(Core),nl |
678 | | ; RR=solution(_) -> Res = possible, |
679 | | (debug_mode(on) -> print('SOLUTION: '),translate:print_bstate(State),nl ; true), |
680 | | ResultState = State |
681 | ? | ; simplify_for_output(RR,Res), ResultState = '$UNKNOWN_STATE' ) |
682 | | ; add_internal_error('Failed to compute feasibility for: ',OpName1), |
683 | | translate:print_bexpr(Pred),nl, |
684 | | Res = internal_error, ResultState = '$UNKNOWN_STATE'). |
685 | | check_if_feasible_operation2(OpName1,UseInvariant,TimeoutFactor,Res,ResultState) :- |
686 | | formatsilent('Guard computation is imprecise for ~w. Not using predicate solver (but B interpreter).~n',[OpName1]), |
687 | | % this version uses sap:testcase_path: this works for things like sequential composition and WHILE loops better |
688 | | get_time_out_with_factor(TimeoutFactor,TO), |
689 | | (UseInvariant=1 -> INV=invariant ; INV=typing), |
690 | | (testcase_path_timeout_catch(INV,TO,[OpName1],b(truth,pred,[]),_Constants,Ops,StateSequence,_TI,R1) |
691 | | -> (is_timeout(R1) -> Res=timeout ; Res = possible), |
692 | | (nonvar(StateSequence),StateSequence=[ResultState|_] -> true ; ResultState = '$UNKNOWN_STATE'), |
693 | | (debug_mode(off) -> true |
694 | | ; print(feasibility_result(OpName1,R1,Ops)),nl, |
695 | | (ResultState = '$UNKNOWN_STATE' -> true |
696 | | ; print('SOLUTION: '),translate:print_bstate(ResultState),nl) |
697 | | ) |
698 | | ; Res=impossible,ResultState = '$UNKNOWN_STATE'). |
699 | | |
700 | | |
701 | | simplify_for_output(no_solution_found(enumeration_warning(_,_,_,_,_)),virtual_time_out). |
702 | | simplify_for_output(no_solution_found(clpfd_overflow),overflow). |
703 | | simplify_for_output(time_out,time_out). |
704 | | |