1 % (c) 2009-2026 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(coverage_statistics,[tcltk_compute_coverage/1, tcltk_compute_coverage_and_enabling/1,
6 tcltk_compute_coverage_and_degree/1,
7 tcltk_compute_min_max/1,
8 pretty_print_min_max_coverage_to_file/1,
9 compute_the_coverage/5,pretty_print_coverage_information_to_file/1,
10 operation_hit/2,query_node_hit/2, uncovered_operation/1,
11 tcltk_analyse_constants/2, tcltk_analyse_constants/1]).
12
13
14 :- use_module(probsrc(module_information)).
15 :- module_info(group,misc).
16 :- module_info(description,'This module is responsible for computing state space coverage info.').
17
18 :- use_module(probsrc(state_space)).
19 :- use_module(probsrc(tools)).
20 :- use_module(probsrc(debug),[debug_println/2, debug_format/3]).
21 :- use_module(probsrc(error_manager),[add_message/4, add_message/3, add_debug_message/4, add_debug_message/3]).
22 :- use_module(probsrc(tools_lists), [length_less/2]).
23 :- use_module(extrasrc(meta_interface),[write_table_to_text_file/2]).
24
25 /* --------------------------------------------------- */
26
27 :- use_module(probsrc(state_space),[current_state_id/1,
28 get_constants_state_for_id/2, try_get_unique_constants_state/1]).
29
30 % provide information about the constants
31 % can be run in procli with -csv constants_analysis
32 tcltk_analyse_constants(Table) :-
33 current_state_id(CurID),
34 tcltk_analyse_constants(CurID,Table).
35
36 :- dynamic constant_can_be_expanded/1.
37
38 :- use_module(library(samsort),[samsort/3]).
39 % called constants_analysis in meta_interface and probcli
40 tcltk_analyse_constants(ID,Table) :-
41 (get_constants_state_for_id(ID,Constants) -> true
42 ; ID=root, try_get_unique_constants_state(Constants)),
43 !,
44 retractall(constant_can_be_expanded(_)),
45 findall(H,get_value_info(H,'$DUMMY$',int(0),_),Header),
46 reorder_cols(Header,RHeader),
47 findall(list([CstID|RInfos]), (member(bind(CstID,CstVal),Constants),
48 findall(Info,get_value_info(_,CstID,CstVal,Info),Infos),
49 reorder_cols(Infos,RInfos),
50 debug_format(19,' ~w : ~w~n',[CstID,RInfos])
51 ), Entries),
52 samsort(samorder,Entries,SEntries),
53 findall(Cst,constant_can_be_expanded(Cst),ExpList),
54 (ExpList=[] -> true
55 ; format(user_output,'Constants that are currently symbolic but could be expanded:~n ~w~n',[ExpList])),
56 Table = list([list(['CONSTANT'|RHeader]) | SEntries]).
57 tcltk_analyse_constants(_,Table) :-
58 Table = list([list(['SETUP CONSTANTS first'])]).
59
60 % move message column to end
61 reorder_cols([Class,MessageCol|OtherCols],ResCols) :- append([Class|OtherCols],[MessageCol],ResCols).
62
63 samorder(list([ID,_,_,TS|_]),list([ID2,_,_,TS2|_])) :- !,(TS,ID) @>= (TS2,ID2).
64 samorder(A,B) :- print(unknown_samorder(A,B)),nl.
65
66 :- use_module(library(terms), [term_size/2]).
67 :- use_module(probsrc(translate), [translate_bvalue_with_limit/3]).
68 get_value_info(class,ID,_,Class) :- get_id_class(ID,Class).
69 get_value_info(INFO,ID,_,Kind) :-
70 is_registered_function_name(ID,_),
71 get_registered_function(_MemoID,ID,RegValue),!, % use stored Memo value instead
72 ? get_value_info2(INFO,ID,RegValue,Kind).
73 get_value_info(INFO,ID,Value,Kind) :-
74 ? get_value_info2(INFO,ID,Value,Kind).
75
76 :- use_module(probsrc(debug),[debug_mode/1]).
77 get_value_info2(Info, ID, Value, Res) :-
78 (var(Info) -> true ; Info=message -> true ; Info=kind),
79 get_value_kind(Value,ID,Kind,Msg),
80 (Info=message,Res=Msg ; Info=kind, Res=Kind).
81 get_value_info2(termsize, _, Value, TS) :- term_size(Value,TS).
82 get_value_info2(value, _, Value, TS) :-
83 (debug_mode(on) -> Limit=1000 ; Limit=100),
84 translate_bvalue_with_limit(Value,Limit,TS).
85
86 % TO DO: use is_concrete_constants_state_id to check if other value exists
87 % TO DO: check if abstract_constants can be converted back
88
89 :- use_module(probsrc(bmachine),[b_is_variable/1,b_is_constant/1]).
90 :- use_module(probsrc(b_machine_hierarchy),[abstract_constant/2]).
91 :- use_module(probsrc(memoization),[is_registered_function_name/2, get_registered_function/3]).
92 :- use_module(probsrc(kernel_objects),[cardinality_as_int/2]).
93
94 get_id_class(ID,Res) :- is_registered_function_name(ID,Expanded),!,
95 (Expanded=true -> Res = 'MEMOIZED (expanded)' ; Res='MEMOIZED'). % probably abstract
96 get_id_class(ID,Res) :- abstract_constant(ID,_),!, Res='ABSTRACT'.
97 get_id_class(ID,Res) :- b_is_constant(ID),!, Res='CONCRETE'.
98 get_id_class(ID,Res) :- b_is_variable(ID),!, Res='VARIABLE'.
99 get_id_class(_,'?').
100
101
102 :- use_module(probsrc(custom_explicit_sets),[is_interval_closure/3, explicit_set_cardinality/2,
103 is_infinite_or_very_large_explicit_set/2,
104 try_expand_custom_set_with_catch/3, is_infinite_explicit_set/1]).
105 :- use_module(library(avl),[avl_size/2]).
106 :- use_module(probsrc(tools_timeout), [time_out_with_factor_call/4]).
107 :- use_module(probsrc(translate), [translate_bvalue_kind/2]).
108
109
110 get_value_kind(CL,_,Res,Msg) :- is_infinite_explicit_set(CL),!,
111 Res = 'INFINITE-Set',
112 Msg = 'The constant is definitely infinite'.
113 get_value_kind(CL,_,Res,Msg) :- is_interval_closure(CL,_,_),!,
114 Res = 'INTERVAL',
115 Msg = 'The constant is a symbolic interval.'.
116 get_value_kind(SimpleValue,_ID,Res,Msg) :- SimpleValue \= closure(_,_,_),
117 translate_bvalue_kind(SimpleValue,R), !,
118 Res=R, Msg=''.
119 get_value_kind(closure(P,T,B),ID,Res,Msg) :-
120 add_debug_message(constants_analysis,'Checking if symbolic closure can be expanded: ',ID,B),
121 % TODO: provide flag as this can take long
122 time_out_with_factor_call(
123 try_expand_custom_set_with_catch(closure(P,T,B),Expansion,get_value_kind),
124 min_max_time_out(10,100,15000), % Factor 10, minimum 100 ms, maximum 15 seconds
125 [silent],
126 (add_debug_message(constants_analysis,'TIME-OUT during expansion for: ',ID),fail)),
127 %functor(Expansion,FF,NN),print(expansion(FF,NN)),nl,
128 %translate:translate_bvalue_with_limit(Expansion,100000,S), write(S),nl,
129 Expansion \= closure(_,_,_),
130 %(length(Expansion,Len) -> print(len(Len)),nl ; true),
131 time_out_with_factor_call(
132 compute_cardinality(closure(P,T,B),Expansion,Size),
133 min_max_time_out(10,100,15000), % Factor=10, but min. 100 ms, max 15000 ms
134 [],
135 (add_debug_message(constants_analysis,'TIME-OUT during computation of Size: ',ID),
136 Size='?')),
137 !, % Symbolic set can be expanded; user could mark it as concrete constant
138 (abstract_constant(ID,_)
139 -> CC = 'Abstract constant ',
140 Suggestion = ' (by moving to CONCRETE_CONSTANTS or annotating with /*@desc expand */ pragma): '
141 ; CC = 'Concrete constant ',
142 Suggestion = ' (by annotating with /*@desc expand */ pragma) '
143 ),
144 ajoin([CC,'is stored symbolically but can be expanded to a finite set of size ',Size,
145 Suggestion ],Msg),
146 get_constant_span(ID,Span),
147 (constant_can_be_expanded(ID) -> true ; assert(constant_can_be_expanded(ID))),
148 add_debug_message(constants_analysis,Msg,ID,Span),
149 ajoin(['FINITE-SYMBOLIC-Set:',Size],Res).
150 get_value_kind(CL,_,Res,Msg) :-
151 is_infinite_or_very_large_explicit_set(CL,20000),!,
152 Res= 'LARGE-SYMBOLIC-Set',
153 Msg = 'The constant has at least 20000 elements'.
154 get_value_kind(closure(_P,_T,_B),_,Res,Msg) :- !,
155 Res = 'SYMBOLIC-Set', Msg=''.
156 get_value_kind(global_set(G),_,Res,Msg) :- !,
157 Res = G, Msg='The constant is a synonym for an enumerated or deferred set'.
158 %TODO: Z freetypes, ...
159 get_value_kind(_,_,'?','').
160
161 compute_cardinality(_,Expansion,Size) :- length_less(Expansion,100),!, cardinality_as_int(Expansion,int(Size)).
162 compute_cardinality(CS,_,Res) :- explicit_set_cardinality(CS,Size),!,Res=Size.
163 compute_cardinality(_,Expansion,Size) :- cardinality_as_int(Expansion,int(Size)). % can be slow for long lists
164
165
166 /* --------------------------------------------------- */
167 :- dynamic var_min_max/3.
168
169 :- use_module(probsrc(bmachine),[get_constant_span/2]).
170
171 tcltk_compute_min_max(Res) :- tcltk_compute_min_max(interesting,Res).
172
173 tcltk_compute_min_max(_,Res) :-
174 ? \+ (visited_expression_id(X), X\=root),!,
175 Res = list([list(['No states visited yet!'])]).
176 tcltk_compute_min_max(Which,_) :-
177 retractall(var_min_max(_,_,_)),
178 ? visited_expression(ID,State), %print(treat(_ID)),nl,
179 (Which = all -> true
180 ; \+ not_interesting(ID)), % only those satisfying scope predicate
181 %functor(State,F,N),print(processing(ID,F,N)),nl,
182 treat_state_for_min_max(State),fail.
183 tcltk_compute_min_max(_,list([Header|Res])) :-
184 Header = list(['Identifier','Type','Min-Value','Max-Value']),
185 findall(Info, get_info(Info,constant), CRes),
186 findall(Info, get_info(Info,variable), VRes),
187 append(CRes,VRes,Res).
188
189 pretty_print_min_max_coverage_to_file(Filename) :-
190 tcltk_compute_min_max(list(Res)),
191 write_table_to_text_file(Filename,Res).
192
193 :- use_module(probsrc(b_machine_hierarchy),[abstract_constant/2,concrete_constant/2]).
194
195 get_info(Info,Type) :-
196 ? var_min_max(Var,Min,Max),
197 ((abstract_constant(Var,_) ; concrete_constant(Var,_))
198 -> Type = constant ; Type = variable),
199 translate_min_max(Var,Min,Max,Type,Info).
200
201 translate_min_max(Var,M,M,Type,Info) :- !,
202 translate:translate_bvalue(M,TMin),
203 Info = list([Var,Type,TMin,'(same value)']).
204 translate_min_max(Var,pred_false,pred_true,Type,Info) :- !,
205 Info = list([Var,Type,'BOOL','(full type)']).
206 translate_min_max(Var,fd(X,T),fd(Y,T),Type,Info) :- !,
207 gen_fd_set(X,Y,T,Set), translate:translate_bvalue(Set,TS),
208 Info = list([Var,Type,TS,'(possible values)']).
209 translate_min_max(Var,Min,Max,Type,Info) :-
210 translate:translate_bvalue(Min,TMin),
211 translate:translate_bvalue(Max,TMax),
212 Info = list([Var,Type,TMin,TMax]).
213
214 gen_fd_set(X,Y,_T,R) :- X>Y,!, R=[].
215 gen_fd_set(X,Y,T,[fd(X,T)|R]) :- X1 is X+1,
216 gen_fd_set(X1,Y,T,R).
217
218 :- use_module(probsrc(specfile),[property/2]).
219 % treat a visited state for min_max
220 treat_state_for_min_max(const_and_vars(_,S)) :- !, treat_state_for_min_max(S).
221 treat_state_for_min_max(concrete_constants(S)) :- !, treat_state_for_min_max(S).
222 treat_state_for_min_max(csp_and_b(_,S)) :- !, treat_state_for_min_max(S).
223 treat_state_for_min_max(root) :- !.
224 treat_state_for_min_max([]) :- !.
225 ?treat_state_for_min_max(List) :- List=[_|_],!,member(bind(VAR,VAL),List), treat_var(VAR,VAL),fail.
226 treat_state_for_min_max(OtherState) :- \+ b_or_z_mode,
227 property(OtherState,'='(Variable,Value)),
228 treat_var(Variable,Value),fail.
229
230 treat_var(Variable,Value) :- retract(var_min_max(Variable,Min,Max)),!,
231 (is_smaller(Value,Min) -> NewMin = Value ; NewMin=Min),
232 (is_smaller(Max,Value) -> NewMax = Value ; NewMax=Max),
233 assertz(var_min_max(Variable,NewMin,NewMax)).
234 treat_var(Variable,Value) :-
235 assertz(var_min_max(Variable,Value,Value)).
236
237 :- use_module(library(avl),[avl_size/2, avl_max/2]).
238 is_smaller(int(X),int(Y)) :- !, X < Y.
239 is_smaller(string(X),string(Y)) :- !, X @< Y.
240 is_smaller(pred_true,_) :- !,fail.
241 is_smaller(pred_false,_) :- !.
242 is_smaller(fd(X,T),fd(Y,T)) :- !, X < Y.
243 is_smaller((X1,X2),(Y1,Y2)) :- !, (is_smaller(X1,Y1) -> true ; X1=Y1, is_smaller(X2,Y2)).
244 is_smaller([],Y) :- !, Y \== [].
245 is_smaller(_,[]) :- !, fail.
246 is_smaller(avl_set(X),avl_set(Y)) :- avl_size(X,SX), avl_size(Y,SY),!,
247 (SX<SY -> true
248 ; SX=SY, avl_max(X,MaxX), avl_max(Y,MaxY),
249 (is_smaller(MaxX,MaxY) % used to be X@<Y
250 -> true
251 ; MaxX=MaxY -> X @< Y) % if same maximum; look at all values; TO DO: maybe look at avl_min first
252 ).
253 is_smaller(X,Y) :- X @< Y.
254
255 /* --------------------------------------------------- */
256
257 pretty_print_coverage_information_to_file(FileName) :-
258 tcltk_compute_coverage(list(Res)),
259 write_table_to_text_file(FileName,Res).
260
261 tcltk_compute_coverage(list(Res)) :-
262 compute_the_coverage(Res,_,_,false,false).
263 tcltk_compute_coverage_and_enabling(list(Res)) :-
264 compute_the_coverage(Res,_,_,true,false).
265 tcltk_compute_coverage_and_degree(list(Res)) :-
266 compute_the_coverage(Res,_,_,false,true).
267
268 compute_the_coverage(Res,TotalNodeNr,TotalTransSum,ShowEnabledInfo,ShowDegreeInfo) :-
269 retractall(operation_hit(_,_,_)),
270 reset_node_hit(ShowEnabledInfo,ShowDegreeInfo),
271 compute_coverage(ShowEnabledInfo,ShowDegreeInfo),
272 findall(list([OpS,Nr]),operation_hit(OpS,Nr),Res1_),
273 sort(Res1_,Res1),
274 length(Res1,NrCovered),
275 findall(list([Prop,Nr]),query_node_hit(Prop,Nr),Res0),
276 findall(list([OpName,'-']), uncovered_operation(OpName),Res2_),
277 sort(Res2_,Res2),
278 length(Res2,NrUncovered),
279 total_number_of_transitions(TotalTransSum),
280 SEPLINE = list(['-------------','']),
281 (ShowEnabledInfo=false
282 -> ResDE = []
283 ;
284 findall(DE, def_enables(DE),EnRes1),
285 findall(DE, pos_enables(DE),EnRes2),
286 append([SEPLINE,list(['DEFINITELY_ENABLED_AFTER',''])|EnRes1],
287 [SEPLINE,list(['POSSIBLY_ENABLES',''])|EnRes2],ResDE)
288 ),
289 (ShowDegreeInfo=false
290 -> Res00 = ResDE
291 ;
292 findall(list([OpDeg,MaxDeg]), max_degree(OpDeg,MaxDeg), DegRes),
293 append([SEPLINE,list(['MAXIMUM_DEGREE',''])|DegRes],ResDE,Res00)
294 ),
295 get_op_string('UNCOVERED_',UNCOVEREDSTR),
296 TOTALSTR = list(['TOTAL_TRANSITIONS',TotalTransSum]), %get_op_string('TOTAL_',TOTALSTR),
297 get_op_string('COVERED_',COVEREDSTR),
298 append([list([UNCOVEREDSTR,NrUncovered])|Res2],Res00,Res01),
299 append([SEPLINE,TOTALSTR,list([COVEREDSTR,NrCovered])|Res1],
300 Res01,OpRes),
301 query_node_hit(total,TotalNodeNr),
302 append([list(['CATEGORY','HITS']),list(['STATES',TotalNodeNr])|Res0],OpRes,Res).
303 %% append(Res_,ResPGE,Res).
304
305 :- use_module(probsrc(specfile),[get_specification_description/2]).
306 get_op_string(PREFIX,Res) :-
307 get_specification_description(operations,OP),
308 string_concatenate(PREFIX,OP,Res).
309
310
311 def_enables(list([OpName1,OpName2])) :- operation_hit(OpName1,_,_),
312 definitely_enabled_after(OpName1,OpName2).
313 pos_enables(list([OpName1,OpName2])) :- operation_hit(OpName1,_,_),
314 possibly_enables(OpName1,OpName2).
315 % \+ definitely_enabled_after(OpName1,OpName2),
316
317 /* note: assumes that same operation name cannot appear with multiple arity */
318
319 total_number_of_transitions(TotalTransSum) :-
320 findall(Nr,operation_hit(_,_,Nr),List),
321 sum_list(List,0,TotalTransSum).
322
323 sum_list([],N,N).
324 sum_list([H|T],SoFar,Res) :- S1 is SoFar+H, sum_list(T,S1,Res).
325
326 operation_hit(OpTerm,Nr) :-
327 ? operation_hit(OpTerm,_ID,Nr).
328
329 :- dynamic operation_hit/3.
330 operation_hit(operation(_),node__2,55).
331
332 :- use_module(probsrc(specfile),[get_possible_event/1]).
333
334 ?uncovered_operation(OpName) :- get_possible_event(OpName),
335 \+(operation_hit(OpName,_,_)).
336
337 add_cov_hit(OpName,ID) :-
338 ((OpName = partition(N,_Op,P)) -> LookupName = partition(N,_,P) ; LookupName = OpName),
339 (retract(operation_hit(LookupName,NID,Nr))
340 -> (N1 is Nr+1, assertz(operation_hit(LookupName,NID,N1)))
341 ; (assertz(operation_hit(OpName,ID,1)))
342 ).
343
344 ?query_node_hit(Prop,Nr) :- node_hit(Prop,Nr).
345 query_node_hit(total,Nr) :- node_hit(open,N1), node_hit(live,N2), node_hit(deadlocked,N3),
346 Nr is N1+N2+N3.
347 :- dynamic node_hit/2.
348 node_hit(property,22).
349 add_node_hit(OpName) :-
350 (retract(node_hit(OpName,Nr))
351 -> N1 is Nr+1, assertz(node_hit(OpName,N1))
352 ; assertz(node_hit(OpName,1))
353 ).
354
355 reset_node_hit(ShowEnabledInfo,ShowDegreeInfo) :-
356 retractall(node_hit(_,_)),
357 assertz(node_hit(deadlocked,0)),
358 (b_or_z_mode -> assertz(node_hit(invariant_violated,0)),
359 assertz(node_hit(invariant_not_checked,0))
360 ; true),
361 assertz(node_hit(open,0)), assertz(node_hit(live,0)),
362 init_enabling_degree_info(ShowEnabledInfo,ShowDegreeInfo).
363
364 :- use_module(probsrc(specfile),[get_operation_name/2]).
365
366 :- use_module(probsrc(state_space_exploration_modes),[is_not_interesting/1]).
367 compute_coverage(_,_) :-
368 ? visited_expression_id(ID),
369 (not_all_transitions_added(ID)
370 -> add_node_hit(open)
371 ? ; (transition(ID,_,_) -> add_node_hit(live)
372 ; is_not_interesting(ID) -> add_node_hit(ignored)
373 ; max_reached_or_timeout_for_node(ID) -> true % e.g., if MAX_OPERATIONS set to 0
374 ; add_node_hit(deadlocked))
375 ),
376 ? (max_reached_or_timeout_for_node(ID)
377 -> add_node_hit(explored_but_not_all_transitions_computed)
378 ; true
379 ),
380 (invariant_violated(ID) -> add_node_hit(invariant_violated) ; true),
381 (time_out_for_invariant(ID) -> add_node_hit(invariant_time_out) ; true),
382 (time_out_for_assertions(ID) -> add_node_hit(assertions_time_out) ; true),
383 ? (invariant_not_yet_checked(ID) -> add_node_hit(invariant_not_checked) ; true),
384 (state_error(ID,_,Err),Err\=invariant_violated ->
385 (Err=abort_error(TYPE,_,_,_)
386 -> add_node_hit(TYPE)
387 ; add_node_hit(state_error))
388 ; true),
389 fail.
390 compute_coverage(ShowEnabledInfo,ShowDegreeInfo) :-
391 ? transition(ID,Operation,NID),
392 nonvar(Operation),
393 get_operation_name(Operation,OpName),
394 add_cov_hit(OpName,ID),
395 (ShowEnabledInfo=true -> update_enabling_info(ID,OpName,NID) ; true),
396 (ShowDegreeInfo=true -> update_degree_info(ID,OpName) ; true),
397 fail.
398 %compute_coverage :- print_message('Covered Properties: '),
399 % node_hit(OpName,Nr),
400 % print(OpName), print(' nodes:'), print(Nr),nl,
401 % fail.
402 %compute_coverage :- print_message('Covered Operations: '),
403 % operation_hit(OpName,_ID,Nr),
404 % print(OpName), print(' hits:'), print(Nr),nl,
405 % fail.
406 %compute_coverage :- nl, print_message('Uncovered Operations: '),
407 % uncovered_operation(OpName),
408 % print(OpName), print(' '), fail.
409 compute_coverage(_ShowEnabledInfo,ShowDegreeInfo) :-
410 (ShowDegreeInfo=true -> finalise_degree_info ; true).
411
412
413
414
415 :- dynamic definitely_enabled_after/2, possibly_enables/2. /* true if after an operation another is always enabled */
416 :- dynamic degree_in_id/3, max_degree/2.
417
418 :- use_module(probsrc(specfile),[b_or_z_mode/0]).
419 :- use_module(probsrc(bmachine),[b_is_operation_name/1]).
420 init_enabling_degree_info(ShowEnabledInfo,_ShowDegreeInfo) :-
421 retractall(definitely_enabled_after(_,_)),
422 retractall(possibly_enables(_,_)),
423 retractall(degree_in_id(_,_,_)),
424 retractall(max_degree(_,_)),
425 b_or_z_mode,
426 ShowEnabledInfo=true,
427 (b_is_operation_name(OpName1) ; OpName1 = '$initialise_machine'),
428 b_is_operation_name(OpName2),
429 assertz(definitely_enabled_after(OpName1,OpName2)),
430 fail.
431 init_enabling_degree_info(_,_).
432
433 finalise_degree_info :- update_last_degree.
434
435 update_enabling_info(_ID,OpName,NewID) :- %print(upd(_ID,OpName)),nl,
436 definitely_enabled_after(OpName,OpName2),
437 (opname_possible(NewID,OpName2)
438 -> true
439 ; retract(definitely_enabled_after(OpName,OpName2))
440 %(retract(definitely_enabled_after(OpName,OpName2)) -> print(retract(OpName,OpName2)))
441 ),fail.
442 update_enabling_info(ID,OpName,NewID) :- %print(upd(_ID,OpName)),nl,
443 opname_possible(NewID,OpName2), % Note: can succeed multiple times with same OpName2 !
444 \+ possibly_enables(OpName,OpName2),
445 \+ opname_possible(ID,OpName2),
446 assertz(possibly_enables(OpName,OpName2)),
447 fail.
448 update_enabling_info(_,_,_).
449
450 % NOTE: we suppose that the transitions are grouped by operation name for each state id; may not be true for CSP ??
451 update_degree_info(ID,OpName) :-
452 get_cur_degree(ID,OpName,Degree), D1 is Degree+1,
453 assertz(degree_in_id(ID,OpName,D1)),
454 fail.
455 update_degree_info(_,_).
456
457 update_last_degree :- (retract(degree_in_id(_,OpName,D)) -> update_degree(OpName,D) ; true).
458
459 get_cur_degree(ID,OpName,Degree) :-
460 (retract(degree_in_id(ID,OpName,D)) -> Degree=D
461 ; update_last_degree,
462 Degree=0).
463 update_degree(OpName,Degree) :-
464 ((max_degree(OpName,Max), Max>=Degree) -> true
465 ; retractall(max_degree(OpName,_)), format('Update degree ~w -> ~w~n',[OpName,Degree]),
466 assertz(max_degree(OpName,Degree))
467 ).
468 opname_possible(ID,OpName) :- transition(ID,Op,_),get_operation_name(Op,OpName).
469
470 % -------------------------------------
471
472