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 |