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(reduce_graph_state_space, [print_state_merge_for_dot/1,print_dot_for_dfa_from_nfa/1, | |
6 | print_min_dfa_for_dot/1, | |
7 | reduce_graph_reset/0, | |
8 | %assert_desired_user_transitions/1, | |
9 | %state_merge_separate_iviolations/1, | |
10 | print_subgraph_associated_with_invariant_violations/1, | |
11 | %assert_goal_nodes/0, | |
12 | %jump_to_next_goal_node/1, | |
13 | print_subgraph_of_goal_nodes/1, | |
14 | set_signature_merge_show_all_transitions/1, | |
15 | signature_merge_show_all_transitions/1, | |
16 | %time_stamp/1, time_between/3, mc_duration/1, | |
17 | %total_visited_states/1, total_transitions/1, | |
18 | %reset_use_all_ops_args/0, | |
19 | ||
20 | extract_graph_of_state_merge/1, | |
21 | ||
22 | ||
23 | % tcltk gui | |
24 | user_made_op_selection/0, user_made_arg_selection/0, set_use_all_operations/2 | |
25 | ||
26 | /* symmetry reduction */ | |
27 | %% cleanup_symmetry/0 /* remove all stored representatives */, | |
28 | %% representative/3 /* get the representative state for this state */, | |
29 | %%% reset_relation_types/0 /* resetting this module for new machine */ | |
30 | ]). | |
31 | ||
32 | /* reduce_graph_state_space.pl - GRAPH REDUCTION FUNCTIONS */ | |
33 | ||
34 | /* List of functions that work and (often) reduce the current state space | |
35 | - print_dot_for_dfa_from_nfa(File). | |
36 | - print_min_dfa_for_dot(File). | |
37 | - print_state_merge_for_dot(File). | |
38 | ||
39 | NB: print_min_dfa_for_dot will perform the nfa -> dfa conversion first, and then | |
40 | try and reduce it. However, this doesn't seem to further reduce the dfa ==> | |
41 | dfa is minimal already | |
42 | */ | |
43 | :- use_module(probsrc(tools)). | |
44 | ||
45 | :- use_module(probsrc(module_information),[module_info/2]). | |
46 | :- module_info(group,dot). | |
47 | :- module_info(description,'Various minimization/reduction algorithms for the state space.'). | |
48 | ||
49 | ||
50 | :- use_module(library(lists)). | |
51 | %:- use_module(library(system)). | |
52 | :- use_module(probsrc(preferences)). | |
53 | :- use_module(probsrc(tools),[atom_or_number_codes/2]). | |
54 | %:- use_module(library(terms)). | |
55 | ||
56 | :- use_module(dotsrc(visualize_graph),[print_graph_header/1, | |
57 | print_graph_footer/0]). | |
58 | :- use_module(probsrc(state_space),[visited_expression/2, visited_expression_id/1, | |
59 | transition/3, | |
60 | invariant_violated/1, not_all_transitions_added/1]). | |
61 | :- use_module(probsrc(specfile),[csp_mode/0]). | |
62 | :- use_module(probsrc(translate),[translate_bvalue/2]). | |
63 | ||
64 | reduce_graph_reset :- assert_desired_user_transitions(alphabet), | |
65 | reset_use_all_ops_args, reset_counter. | |
66 | ||
67 | :- use_module(probsrc(eventhandling),[register_event_listener/3]). | |
68 | :- register_event_listener(clear_specification,reduce_graph_reset, | |
69 | 'Reset reduce_graph_state_space.'). | |
70 | ||
71 | :- set_prolog_flag(double_quotes, codes). | |
72 | ||
73 | /*============================================================================== | |
74 | Utility Functions | |
75 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
76 | ||
77 | ||
78 | % different types of graphs | |
79 | graph_type(signature_merge). | |
80 | graph_type(dfa_abstraction). | |
81 | ||
82 | ||
83 | % flatten list | |
84 | ent03r_flatten([],[]) :- !. | |
85 | ent03r_flatten([X|L],R) :- !, ent03r_flatten(X,XR), ent03r_flatten(L,LR), append(XR,LR,R). | |
86 | ent03r_flatten(X,[X]). | |
87 | ||
88 | ||
89 | ent03r_union([],X,X). | |
90 | ent03r_union([X|R],Y,Z) :- member(X,Y),!,ent03r_union(R,Y,Z). | |
91 | ent03r_union([X|R],Y,[X|Z]) :- ent03r_union(R,Y,Z). | |
92 | ||
93 | :- volatile curr_equivalence_class_id/1. | |
94 | :- dynamic curr_equivalence_class_id/1. | |
95 | ||
96 | % initialise id counter | |
97 | init_equivalence_class_id :- | |
98 | (curr_equivalence_class_id(X) -> | |
99 | retract(curr_equivalence_class_id(X)) | |
100 | ;true), | |
101 | assertz(curr_equivalence_class_id(0)). | |
102 | ||
103 | % get a unique id | |
104 | next_equivalence_class_id(ID) :- | |
105 | (curr_equivalence_class_id(X) -> | |
106 | ID is X+1, | |
107 | retract(curr_equivalence_class_id(X)), | |
108 | assertz(curr_equivalence_class_id(ID)) | |
109 | ; ID=0, assertz(curr_equivalence_class_id(ID))). | |
110 | ||
111 | number_of_equivalence_classes(X) :- curr_equivalence_class_id(X). | |
112 | % a counter; to do: use extension(counter) | |
113 | increment_counter(C) :- | |
114 | (counter(D) -> C is D + 1,retract(counter(_)),assertz(counter(C));C is 0, | |
115 | assertz(counter(C))). | |
116 | reset_counter :- (counter(_) -> retract(counter(_));true),assertz(counter(0)). | |
117 | ||
118 | ||
119 | ||
120 | ||
121 | transition(State,TranEncoded,IndividualSig,Succ) :- | |
122 | transition(State,TranEncoded,Succ), | |
123 | transition_info(TranEncoded,_,_,IndividualSig). | |
124 | ||
125 | transition_info('-->'(FAction,Args), Name, Arity, Signature) :- | |
126 | transition_name_arity('-->'(FAction,Args),Name,Arity), %Arity is -(Arity+1), | |
127 | compute_signature(Name,Arity,Signature),!. | |
128 | transition_info(io(V,Channel,_Span),Name,Arity,Signature) :- | |
129 | csp_mode, | |
130 | atomic(Channel), length(V,Arity), Name = Channel, | |
131 | compute_signature(Name,Arity,Signature),!. | |
132 | transition_info(Action, Name, Arity, Signature) :- nonvar(Action), functor(Action,Name,Arity), | |
133 | compute_signature(Name,Arity,Signature),!. | |
134 | transition_info(_, Name, Arity, Signature) :- Name = 'err:', Arity = ('?'), Signature = 'err'. | |
135 | ||
136 | :- volatile transition_info/3. | |
137 | :- dynamic transition_info/3. | |
138 | ||
139 | retractall_transition_info :- | |
140 | retractall(transition_info(_,_,_)). | |
141 | ||
142 | % leuschel Jan 2013: I don't understand this piece of code below: | |
143 | compute_signature(Name, Arity, Signature) :- | |
144 | (transition_info(Name,Arity,Signature) -> | |
145 | true | |
146 | ; FS_cl = "/", | |
147 | /* Arity for operations that return a value is one less than it | |
148 | actually is: so that later predicates can test for a return | |
149 | value by the arity being a negative number; and being one less | |
150 | enables coping with returning operations with 0 arity. */ | |
151 | (Arity < 0 -> | |
152 | RealArity is (-Arity-1), Arrow_cl = "-->", | |
153 | number_codes(1, Return_Len), | |
154 | LP_cl = "(", RP_cl = ")" | |
155 | ; RealArity is Arity, Arrow_cl = [], | |
156 | Return_Len = [], LP_cl = [], RP_cl = []), | |
157 | atom_or_number_codes(Name, Nn), number_codes(RealArity, Aa), | |
158 | NnAa = [Nn, FS_cl, Aa], | |
159 | ent03r_flatten(NnAa, F_cl), | |
160 | RES = [F_cl, Arrow_cl, LP_cl, Return_Len, RP_cl], | |
161 | ent03r_flatten(RES, Flat_RES), atom_codes(Signature, Flat_RES), | |
162 | assertz(transition_info(Name,Arity,Signature))), | |
163 | !. | |
164 | ||
165 | % find the name and arity of a transition | |
166 | transition_name_arity('-->'(FAction,_), Name, Arity) :- nonvar(FAction), | |
167 | functor(FAction,Name,Arity),!. | |
168 | transition_name_arity(Action, Name, Arity) :- nonvar(Action), functor(Action,Name,Arity),!. | |
169 | transition_name_arity(_, Name, Arity) :- Name = 'err:', Arity = 'err'. | |
170 | ||
171 | % get the transition's arguments from it | |
172 | get_transition_arguments('-->'(FAction,_),Args) :- | |
173 | nonvar(FAction),FAction =.. [_|Args],!. | |
174 | get_transition_arguments(Action, Args) :- nonvar(Action), Action =.. [_|Args],!. | |
175 | get_transition_arguments(_, Args) :- Args = 'err'. | |
176 | ||
177 | ||
178 | % convert the transition's arguments to a list of Strings | |
179 | get_args_as_strings([],[]). | |
180 | get_args_as_strings([First|SecondPlus],[String|Tail]) :- | |
181 | translate_bvalue(First,String), | |
182 | get_args_as_strings(SecondPlus,Tail). | |
183 | ||
184 | % convert a list of atoms to a flat list of codes representing them separated | |
185 | % by a Delimitor string. | |
186 | convert_string_to_code_list(ListA, CodeListA, Delimitor) :- | |
187 | atom_codes(Delimitor,DelimitorCodeList), | |
188 | convert_string_to_code_list_helper(ListA, CodeListA,DelimitorCodeList). | |
189 | convert_string_to_code_list_helper([],[],_). | |
190 | convert_string_to_code_list_helper([H|T],[Hhh|Tt],DelimitorCodeList_) :- | |
191 | (T == [] -> | |
192 | DelimitorCodeList = [];DelimitorCodeList = DelimitorCodeList_), | |
193 | atom_codes(H,Hh),append(Hh,DelimitorCodeList,Hhh), | |
194 | convert_string_to_code_list_helper(T,Tt,DelimitorCodeList). | |
195 | ||
196 | % get the alphabet for the state space, for a particular graph type, taking into | |
197 | % account the "don't cares" for transitions. | |
198 | get_alphabet(GT, Alphabet) :- | |
199 | findall(Letter,(transition(_,L,_), %transition_for_user(L,LU), | |
200 | remove_transition_arg_dont_cares(GT,L,Letter)),Alphabet_), | |
201 | remove_dups(Alphabet_,Alphabet). | |
202 | ||
203 | ||
204 | % get the signatures of alphabet for the state space. | |
205 | get_alphabet_signatures(Alphabet) :- | |
206 | findall(Letter,(transition(_,L,_),transition_info(L,_,_,Letter)),Alphabet1), | |
207 | remove_dups(Alphabet1,Alphabet). | |
208 | ||
209 | ||
210 | :- volatile transition_arg_dont_cares/3. | |
211 | :- dynamic transition_arg_dont_cares/3. | |
212 | ||
213 | retractall_transition_arg_dont_cares :- | |
214 | retractall(transition_arg_dont_cares(_,_,_)). | |
215 | ||
216 | ||
217 | % make a list of size, Length containing ascending integers, except at indices | |
218 | % specified by DontCares, where the element is a '_'. Result is List. | |
219 | make_counting_list(Length,DontCares,List) :- | |
220 | make_counting_list(0,Length,DontCares,List). | |
221 | make_counting_list(End,End,_,[]) :- !. | |
222 | make_counting_list(Counter,Length,DontCares,[Head|Tail]) :- | |
223 | ? | (member(Counter,DontCares) -> |
224 | Head = '_' | |
225 | ; Head is Counter), | |
226 | CounterAddOne is (Counter + 1), | |
227 | make_counting_list(CounterAddOne,Length,DontCares,Tail). | |
228 | ||
229 | % Assert "don't cares" format for a transition: '_' => don't care, and | |
230 | % anything but this implies that we're intested in this element: GT is the | |
231 | % type of graph we're asserting for. | |
232 | assert_transition_dont_cares(GT,TransitionSig, DontCareIndices) :- | |
233 | retractall(transition_arg_dont_cares(GT,TransitionSig,_)), | |
234 | assertz(transition_arg_dont_cares(GT,TransitionSig, DontCareIndices)). | |
235 | ||
236 | % Assert that all arguments of all UNKNOWN transitions are not cared about - | |
237 | % so this can be used to set dont cares for new transitions of the state space, | |
238 | % without changing current dont cares. | |
239 | assert_all_transition_arg_dont_cares :- | |
240 | ? | graph_type(GT), |
241 | ? | transition(_,Trans,_),transition_info(Trans,_,ArgsLength,Sig), |
242 | (transition_arg_dont_cares(GT,Sig,_) -> fail;true), | |
243 | % check if argslength is < 0, then add one if it is. | |
244 | ((ArgsLength < 0) -> | |
245 | ArgLength is (-ArgsLength-1) | |
246 | ; ArgLength is ArgsLength), | |
247 | make_counting_list(ArgLength,[],DontCares), | |
248 | make_counting_list(ArgLength,DontCares,ArgFormat), | |
249 | assert_transition_dont_cares(GT,Sig,ArgFormat),fail. | |
250 | assert_all_transition_arg_dont_cares. | |
251 | ||
252 | % Assert that ALL arguments of ALL transitions are not cared about - | |
253 | % this writes over whatever dont cares had been set before. | |
254 | assert_all_transition_arg_dont_cares(ignore_previous_dont_cares) :- | |
255 | retractall_transition_arg_dont_cares, | |
256 | graph_type(GT), | |
257 | transition(_,Trans,_),transition_info(Trans,_,ArgsLength,Sig), | |
258 | % check if argslength is < 0, then add one if it is. | |
259 | ((ArgsLength < 0) -> | |
260 | ArgLength is (-ArgsLength-1) | |
261 | ; ArgLength is ArgsLength), | |
262 | make_counting_list(ArgLength,[],DontCares), | |
263 | make_counting_list(ArgLength,DontCares,ArgFormat), | |
264 | assert_transition_dont_cares(GT,Sig,ArgFormat),fail. | |
265 | assert_all_transition_arg_dont_cares(ignore_previous_dont_cares). | |
266 | ||
267 | % predicate to filter out the arguments from a transition we don't care about. | |
268 | remove_transition_arg_dont_cares(_GT, Tran, FilteredTran) :- | |
269 | csp_mode, Tran = io(V,Ch,_),!, | |
270 | length(V,Len), gen_underscores(Len,US), | |
271 | FilteredTran =.. [Ch|US]. | |
272 | remove_transition_arg_dont_cares(GT, Tran, FilteredTran) :- | |
273 | get_transition_arguments(Tran,Args),transition_info(Tran,N,Arity,Sig), | |
274 | atom_codes(N,Name), | |
275 | transition_arg_dont_cares(GT,Sig,DontCares), | |
276 | get_args_as_strings(Args,ArgsS), | |
277 | remove_transition_arg_dont_cares3(DontCares,ArgsS,Filtered), | |
278 | convert_string_to_code_list(Filtered,FilteredStrings,','), | |
279 | (Arity < 0 -> | |
280 | atom_codes('-->(1)',Arrow_cl) | |
281 | ; Arrow_cl = []),atom_codes('(',LP),atom_codes(')',RP), | |
282 | ent03r_flatten([Name,LP,FilteredStrings,RP,Arrow_cl],FilteredTranCodeList), | |
283 | atom_codes(FilteredTranName,FilteredTranCodeList), | |
284 | FilteredTranName = FilteredTran. % put unification afterwards in case FilteredTran is already instantiated and not atomic | |
285 | ||
286 | gen_underscores(0,R) :- !,R=[]. | |
287 | gen_underscores(N,['_'|T]) :- N1 is N-1, gen_underscores(N1,T). | |
288 | % given 3 lists, DontCares, Original, Result, put each element of Original | |
289 | % into the same index in Result, unless the same element in DontCares is '_', in | |
290 | % which case put '_' in Result. | |
291 | remove_transition_arg_dont_cares3([],[],[]) :- !. | |
292 | remove_transition_arg_dont_cares3(['_'|Tail1], [_|Tail2], ['_'|Rest]) :- | |
293 | !, remove_transition_arg_dont_cares3(Tail1,Tail2,Rest). | |
294 | remove_transition_arg_dont_cares3([_|Tail1], [G|Tail2], [G|Rest]) :- | |
295 | !, remove_transition_arg_dont_cares3(Tail1,Tail2,Rest). | |
296 | ||
297 | :- volatile desired_user_transition/2. | |
298 | :- dynamic desired_user_transition/2. | |
299 | ||
300 | % Assert that the user wants to see all of the transitions in the alphabet, and | |
301 | % remove information stored about transition argument dont cares. (use on loading machine). | |
302 | assert_desired_user_transitions(alphabet) :- | |
303 | retractall(desired_user_transition(_,_)), | |
304 | retractall_transition_info,retractall_transition_arg_dont_cares, | |
305 | assert_all_transition_arg_dont_cares,get_alphabet_signatures(Ab),/* print(Ab),nl, */ | |
306 | ? | graph_type(GT),member(X,Ab), |
307 | assertz(desired_user_transition(GT,X)),fail. | |
308 | assert_desired_user_transitions(alphabet). | |
309 | ||
310 | % Assert that the user wants to see all of the transitions in the alphabet. | |
311 | assert_desired_user_transitions(only_alphabet) :- | |
312 | retractall(desired_user_transition(_,_)),get_alphabet_signatures(Ab), | |
313 | graph_type(GT),member(X,Ab), | |
314 | assertz(desired_user_transition(GT,X)),fail. | |
315 | assert_desired_user_transitions(only_alphabet). | |
316 | ||
317 | % Assert that the user wants to see all of the transitions in the alphabet, for | |
318 | % a particular graph type. | |
319 | assert_desired_user_transitions(GT,only_alphabet) :- | |
320 | retractall(desired_user_transition(GT,_)),get_alphabet_signatures(Ab), | |
321 | graph_type(GT),member(X,Ab), | |
322 | assertz(desired_user_transition(GT,X)),fail. | |
323 | assert_desired_user_transitions(_,only_alphabet). | |
324 | ||
325 | ||
326 | :- dynamic goal_nodes/1. | |
327 | ||
328 | :- use_module(probsrc(model_checker),[node_satisfies_goal/1]). | |
329 | assert_goal_nodes :- | |
330 | retractall(goal_nodes(_)),findall(X,(visited_expression_id(X), | |
331 | node_satisfies_goal(X)),R),sort(R,Rr),assertz(goal_nodes(Rr)). | |
332 | ||
333 | ||
334 | :- dynamic use_all_operations/2,use_no_arguments/2,user_has_made_op_selection/1, | |
335 | user_has_made_arg_selection/1. | |
336 | reset_use_all_ops_args :- | |
337 | retractall(use_all_operations(_,_)), | |
338 | retractall(use_no_arguments(_,_)), | |
339 | retractall(user_has_made_op_selection(_)), | |
340 | retractall(user_has_made_arg_selection(_)), | |
341 | assertz(use_all_operations(signature_merge,yes)), | |
342 | assertz(use_no_arguments(signature_merge,yes)), | |
343 | assertz(use_all_operations(dfa_abstraction,yes)), | |
344 | assertz(use_no_arguments(dfa_abstraction,yes)), | |
345 | assertz(user_has_made_op_selection(no)), | |
346 | assertz(user_has_made_arg_selection(no)). | |
347 | ||
348 | user_made_op_selection :- | |
349 | (user_has_made_op_selection(yes) -> | |
350 | true | |
351 | ; retractall(user_has_made_op_selection(no)),print('change just made'),nl, | |
352 | assertz(user_has_made_op_selection(yes))). | |
353 | user_made_arg_selection :- | |
354 | (user_has_made_arg_selection(yes) -> | |
355 | true | |
356 | ; retractall(user_has_made_arg_selection(no)), | |
357 | assertz(user_has_made_arg_selection(yes))). | |
358 | ||
359 | set_use_all_operations(Type, Yes) :- | |
360 | retractall(use_all_operations(Type,_)), | |
361 | ((Yes == yes) -> | |
362 | assertz(use_all_operations(Type,yes)), | |
363 | assert_desired_user_transitions(Type,only_alphabet) | |
364 | ; assertz(use_all_operations(Type,no))). | |
365 | set_use_no_arguments(Type, Yes) :- | |
366 | retractall(use_no_arguments(Type,_)), | |
367 | ((Yes == yes) -> | |
368 | assertz(use_no_arguments(Type,yes)), | |
369 | assert_all_transition_arg_dont_cares(ignore_previous_dont_cares) | |
370 | ; assertz(use_no_arguments(Type,no))). | |
371 | ||
372 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
373 | End utility functions | |
374 | ==============================================================================*/ | |
375 | ||
376 | ||
377 | ||
378 | /*============================================================================== | |
379 | DFA minimisation applied and modified to prob state space graphs | |
380 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
381 | ||
382 | :- use_module(library(ordsets)). | |
383 | ||
384 | :- dynamic distinguished/3,table_marked/1,equivalence_clazz/1,dfa_states_/2, | |
385 | dfa_tran_/3,final_states/1,min_dfa_state/2. | |
386 | ||
387 | dfa_cleanup :- | |
388 | retractall(equivalence_clazz(_)),retractall(distinguished(_,_,_)), | |
389 | retractall(table_marked(_)),retractall(min_dfa_state(_,_)). | |
390 | ||
391 | init :- | |
392 | dfa_cleanup,assertz(table_marked(no)),get_dfa_states(States),init(States). | |
393 | ||
394 | init([_|[]]). | |
395 | init([H|T]) :- | |
396 | setup_distinguished_table(H, T), | |
397 | init(T). | |
398 | ||
399 | setup_distinguished_table(_, []). | |
400 | setup_distinguished_table(X, [H|T]) :- | |
401 | final_states(Final_States), | |
402 | (((member(X, Final_States), \+member(H, Final_States)) | (\+member(X, Final_States), member(H, Final_States))) -> | |
403 | assertz(distinguished(X,H,yes)),retractall(table_marked(_)), assertz(table_marked(yes)) | |
404 | ; assertz(distinguished(X,H,no))), | |
405 | setup_distinguished_table(X,T). | |
406 | ||
407 | ||
408 | construct_distinguished_table :- | |
409 | distinguished(A,B,no), | |
410 | dfa_tran_(A,X,Aprime), | |
411 | dfa_tran_(B,X,Bprime), | |
412 | %print('('),print(A),print(','),print(B),print(' = no)'),print(' '),print(X),print(' ('),print(Aprime),print(','),print(Bprime),print(')'),nl, | |
413 | ((distinguished(Aprime,Bprime, yes) | distinguished(Bprime,Aprime,yes)) -> | |
414 | retract(distinguished(A,B,_)), assertz(distinguished(A,B, yes)), | |
415 | retractall(table_marked(_)), assertz(table_marked(yes)) | |
416 | ,print(A),print(' '),print(B),print(' '),print(yes),nl | |
417 | ;true), | |
418 | fail. | |
419 | construct_distinguished_table :- | |
420 | (table_marked(yes) -> | |
421 | retractall(table_marked(_)), assertz(table_marked(no)), construct_distinguished_table;true). | |
422 | ||
423 | assert_equivalence_clazz(Class, []) :- | |
424 | ((\+equivalence_clazz(Class),equivalence_clazz(Any),ord_intersection(Class,Any,[])) | |
425 | -> assertz(equivalence_clazz(Class)) | |
426 | ; true). | |
427 | assert_equivalence_clazz(Class, [H|T]) :- | |
428 | ((ord_intersection(Class, H, [_|_]) -> | |
429 | ord_union(Class, H, Res),assert_equivalence_clazz(Res, T)) | |
430 | ; assert_equivalence_clazz(Class, T)). | |
431 | ||
432 | assert_equivalence_clazzes_([]). | |
433 | assert_equivalence_clazzes_([H|T]) :- | |
434 | assert_equivalence_clazz(H, T), | |
435 | assert_equivalence_clazzes_(T). | |
436 | ||
437 | assert_equivalence_clazzes :- | |
438 | findall([X,Y],distinguished(X,Y,'no'),Res), | |
439 | sort(Res, Result), | |
440 | assert_equivalence_clazzes_(Result), | |
441 | fail. | |
442 | assert_equivalence_clazzes. | |
443 | ||
444 | unflatten([], []). | |
445 | unflatten([H|T], [[H]|Res]) :- | |
446 | unflatten(T, Res). | |
447 | ||
448 | minimize_dfa(FullResult) :- | |
449 | init, construct_distinguished_table, assert_equivalence_clazzes, | |
450 | findall(X,equivalence_clazz(X),ReS),% retractall(equivalence_clazz(_)), | |
451 | findall([X,Y],distinguished(X,Y,'yes'),Result), | |
452 | %print(Result),nl, | |
453 | ent03r_flatten(Result, Flat_Re),remove_dups(Flat_Re, Resu), | |
454 | sort(Resu, Result_), | |
455 | %print('*'),print(Result_),nl, | |
456 | ent03r_flatten(ReS, Flat_Res),sort(Flat_Res,Flat_Res_), | |
457 | %print(Flat_Res_),nl, | |
458 | length(Result_,LR),length(Flat_Res_,FR), | |
459 | (LR =< FR -> | |
460 | ord_intersection(Result_, Flat_Res_,_,Diff) | |
461 | ; ord_intersection(Flat_Res_, Result_,_,Diff)), | |
462 | %print(Diff),nl, | |
463 | unflatten(Diff, Fat_Diff),append(ReS, Fat_Diff, FullResult),assert_min_dfa_states(FullResult). | |
464 | ||
465 | get_representative_from_list(L,Representative) :- | |
466 | member(Representative, L),!. | |
467 | ||
468 | assert_min_dfa_states([]). | |
469 | assert_min_dfa_states([H|T]) :- | |
470 | get_representative_from_list(H,Represen), | |
471 | assertz(min_dfa_state(H,Represen)), | |
472 | assert_min_dfa_states(T). | |
473 | ||
474 | print_min_dfa_states_representatives_for_dot :- | |
475 | min_dfa_state(_,ID), | |
476 | map_dfa_state_to_id(RealRep,ID), | |
477 | (RealRep == [] -> fail;true), | |
478 | (RealRep == [root] -> | |
479 | print(ID),print('[shape=invtriangle, color=green, label=""];'),nl,fail %"#0d9d03" | |
480 | ; print(ID), print('[shape=ellipse, color=black, label=\"'),print(ID), | |
481 | print('\", id='),print(ID),print('];'),nl,fail). | |
482 | print_min_dfa_states_representatives_for_dot. | |
483 | ||
484 | print_min_dfa__representatives_transitions_ :- | |
485 | min_dfa_state(_,Representative), | |
486 | dfa_tran_(Representative,Letter,Succ), | |
487 | min_dfa_state(State,SuccRepresentative), | |
488 | ||
489 | map_dfa_state_to_id(RealRep,Representative), | |
490 | RealRep \== [], | |
491 | map_dfa_state_to_id(RealSuccRep,SuccRepresentative), | |
492 | RealSuccRep \== [], | |
493 | ||
494 | member(Succ,State), | |
495 | print(Representative), | |
496 | print(' -> '), | |
497 | print(SuccRepresentative), | |
498 | (RealRep == [root] -> | |
499 | print('[style=dotted, color=black, label=\"') | |
500 | ; print('[color=blue, label=\"')), | |
501 | print(Letter), | |
502 | print('\"];'), | |
503 | nl, | |
504 | fail. | |
505 | print_min_dfa__representatives_transitions_. | |
506 | ||
507 | print_min_dfa_for_dot(File) :- | |
508 | nfa_to_dfa, | |
509 | minimize_dfa(_), | |
510 | % for each min_dfa_state, get a representative state, for each letter of the alphabet, print the transition | |
511 | % and the representative state to which the transition leads | |
512 | tell(File), | |
513 | print_graph_header(minimum_dfa), | |
514 | print_min_dfa_states_representatives_for_dot, | |
515 | print_min_dfa__representatives_transitions_, | |
516 | print_graph_footer, | |
517 | told. | |
518 | print_min_dfa_for_dot(_). | |
519 | ||
520 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
521 | End DFA minimisation | |
522 | ==============================================================================*/ | |
523 | ||
524 | /*============================================================================== | |
525 | NFA to DFA | |
526 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
527 | ||
528 | % Use this to find out about all of the transitions - instead of using transition/3 directly. This predicate filters | |
529 | % out transitions which the user isn't interested in. | |
530 | nfa_to_dfa_transition_filter(A,B,Sig,D) :- | |
531 | transition(A,B,D), | |
532 | transition_info(B,_,_,SmallSig), | |
533 | remove_transition_arg_dont_cares(dfa_abstraction, B, Sig), | |
534 | desired_user_transition(dfa_abstraction,SmallSig). | |
535 | ||
536 | :- volatile transi_/2,map_dfa_state_to_id/2. | |
537 | :- dynamic transi_/2,map_dfa_state_to_id/2. | |
538 | findall_transitions(State,TransName,_) :- | |
539 | nfa_to_dfa_transition_filter(State,_,TransName,Succ), | |
540 | (transi_(State,Succ) -> | |
541 | true | |
542 | ; asserta(transi_(State,Succ))), | |
543 | fail. | |
544 | findall_transitions(State,_,Result) :- | |
545 | findall(Succ,transi_(State,Succ),Result),retractall(transi_(_,_)). | |
546 | ||
547 | nfa_cleanup :- | |
548 | retractall(dfa_states_(_,_)),retractall(dfa_tran_(_,_,_)), | |
549 | retractall(final_states(_)),retractall(map_dfa_state_to_id(_,_)). | |
550 | ||
551 | move_([],_,[]). | |
552 | move_([Head|Tail],T,R) :- | |
553 | move_(Head,T,R1), | |
554 | move_(Tail,T,R2),ent03r_union(R1,R2,R). | |
555 | move_(S,T,R) :- | |
556 | findall_transitions(S,T,R). | |
557 | move(S,T,R) :- move_(S,T,Rp), | |
558 | retractall(transi_(_,_)),ent03r_flatten(Rp,R),!. | |
559 | ||
560 | nfa_to_dfa_worker(Alphabet) :- | |
561 | repeat, | |
562 | (dfa_states_(S,no) -> true;!,fail), | |
563 | %print('testing S = '),print(S),nl, | |
564 | retract(dfa_states_(S,no)), | |
565 | asserta(dfa_states_(S,yes)), | |
566 | % print(treating_dfa_state(S)),nl, | |
567 | (map_dfa_state_to_id(S,_) -> | |
568 | true | |
569 | ; next_equivalence_class_id(ID0), | |
570 | % print(new_equiv_class(ID0)),nl, | |
571 | asserta(map_dfa_state_to_id(S,ID0))), | |
572 | %print('*'), | |
573 | member(Letter,Alphabet), | |
574 | % print(computing_move(S,Letter)),nl, | |
575 | move(S,Letter,Res), | |
576 | sort(Res,Result), | |
577 | % print(move(S,Letter,Res)),nl, | |
578 | (dfa_states_(Result,_) -> | |
579 | true /* state already exists */ | |
580 | ; asserta(dfa_states_(Result,no)), % register new, untreated state | |
581 | % print(new_successor_state(Result)),nl, | |
582 | (map_dfa_state_to_id(Result,_) -> | |
583 | true | |
584 | ; next_equivalence_class_id(ID1), | |
585 | % print(new_equiv_class(ID1)),nl, | |
586 | asserta(map_dfa_state_to_id(Result,ID1)))), | |
587 | %print('asserting '),print(Result),nl), | |
588 | map_dfa_state_to_id(S,ID2), | |
589 | map_dfa_state_to_id(Result,ID3), | |
590 | % print(dfa_tran(ID2,Letter,ID3)),nl, | |
591 | asserta(dfa_tran_(ID2,Letter,ID3)),fail. | |
592 | nfa_to_dfa_worker(_). | |
593 | ||
594 | get_dfa_states(States) :- | |
595 | findall(S,map_dfa_state_to_id(_,S),Statess), | |
596 | sort(Statess,States). | |
597 | ||
598 | ||
599 | ||
600 | nfa_to_dfa :- | |
601 | nfa_cleanup, | |
602 | init_equivalence_class_id, | |
603 | /* findall(X,desired_user_transition(dfa_abstraction,X),Alphabet), */ | |
604 | get_alphabet(dfa_abstraction,Alphabet), | |
605 | print('% Alphabet: '), print(Alphabet),nl, | |
606 | asserta(dfa_states_([root],no)), | |
607 | nfa_to_dfa_worker(Alphabet). | |
608 | nfa_to_dfa :- | |
609 | get_dfa_states(States), | |
610 | map_dfa_state_to_id([root],ID1), | |
611 | (map_dfa_state_to_id([],ID2) -> | |
612 | List = [ID2,ID1], %add other final states here | |
613 | sort(List,SList), | |
614 | ord_subtract(States,SList,Finals) | |
615 | ; ord_subtract(States,[ID1],Finals)), | |
616 | ||
617 | assertz(final_states(Finals)). | |
618 | ||
619 | ||
620 | ||
621 | print_dfa_from_nfa_states_id :- | |
622 | map_dfa_state_to_id(Reals,ID), | |
623 | ((Reals == []) -> fail;true), | |
624 | ||
625 | % work out outline colour | |
626 | ((member(A,Reals),transition(A,_,_)) -> % this node not is open (**in this dfa graph) | |
627 | get_preference(dot_normal_node_colour,OutColour) | |
628 | ; get_preference(dot_open_node_colour,OutColour)), % this node is open | |
629 | ||
630 | % work out fill colour | |
631 | ((invariant_violated(C), member(C,Reals)) -> | |
632 | get_preference(dot_invariant_violated_node_colour,InColour) | |
633 | ; true), | |
634 | ||
635 | % work out shape | |
636 | ((Reals == [root]) -> % its the root | |
637 | get_preference(dot_root_shape,Shape), | |
638 | print(root),print('[id="root", shape='),print(Shape),print(', color=green, label=""];'),nl,fail | |
639 | ; ((state_space:current_state_id(B),member(B,Reals)) -> % it contains the current state | |
640 | get_preference(dot_current_node_shape,Shape) | |
641 | ; get_preference(dot_normal_node_shape,Shape))), % it doesn't contain the current state | |
642 | ||
643 | % do the printing | |
644 | print(ID),print('[id='),print(ID),print(', shape='),print(Shape),print(', color=\"'),print(OutColour),print('\"'), | |
645 | (nonvar(InColour) -> print(', style=filled, fillcolor='),print(InColour); true),print('];'),nl, | |
646 | fail. | |
647 | print_dfa_from_nfa_states_id. | |
648 | ||
649 | print_dot_for_dfa_from_nfa_ :- | |
650 | dfa_tran_(ID1,Letter,ID2), | |
651 | map_dfa_state_to_id(ID1_reals, ID1), | |
652 | map_dfa_state_to_id(ID2_reals, ID2), | |
653 | ((ID2_reals == []) -> fail;true), | |
654 | ((ID1_reals == [root]) -> | |
655 | print(root) | |
656 | ; print(ID1)), | |
657 | print(' -> '), | |
658 | print(ID2), | |
659 | (map_dfa_state_to_id([root], ID1) -> | |
660 | print('[color=black, label=\"') %style=dotted, | |
661 | ; ((ID1_reals \== ID2_reals,ord_subset(ID2_reals, ID1_reals)) -> | |
662 | print('[style=dotted,') | |
663 | ; print('[')), | |
664 | print('color=blue, label=\"')), | |
665 | print(Letter), | |
666 | print('\"];'), | |
667 | nl, | |
668 | fail. | |
669 | print_dot_for_dfa_from_nfa_. | |
670 | ||
671 | print_dot_for_dfa_from_nfa(File) :- | |
672 | % use all operations if desired | |
673 | (use_all_operations(dfa_abstraction,yes) -> | |
674 | set_use_all_operations(dfa_abstraction,yes) | |
675 | ; true), | |
676 | % use no arguments if desired | |
677 | (use_no_arguments(dfa_abstraction,yes) -> | |
678 | set_use_no_arguments(dfa_abstraction,yes) | |
679 | ; true), | |
680 | print('% Converting NFA to DFA'),nl, | |
681 | nfa_to_dfa, | |
682 | number_of_equivalence_classes(Nr), | |
683 | format('% Writing DFA with ~w states to file: ~w~n',[Nr,File]), | |
684 | tell(File), | |
685 | print_graph_header(nfa_to_dfa), | |
686 | print_dfa_from_nfa_states_id, | |
687 | nl, | |
688 | print_dot_for_dfa_from_nfa_, | |
689 | print_graph_footer, | |
690 | told, | |
691 | (Nr>100 -> format('% Warning: viewing ~w states with dot can take time!~n',[Nr]) ; true). | |
692 | ||
693 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
694 | End of NFA to DFA code | |
695 | ==============================================================================*/ | |
696 | ||
697 | ||
698 | ||
699 | /*============================================================================== | |
700 | Signature Merge based on a state's transitions | |
701 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | |
702 | ||
703 | :- dynamic counter/1,state_signature/4, | |
704 | state_merge_equivalence_class/3,transition_printed/1,state_printed/1, | |
705 | %state_merge_filter_mode/1, | |
706 | state_merge_separate_invariant_violations/1,non_det_trans/3, | |
707 | signature_merge_show_all_transitions/1,non_def_trans/2. | |
708 | ||
709 | % Either no or yes. The former means we don't separate states that break the | |
710 | % invariant violation into their own equivalence class. The latter means we do. | |
711 | state_merge_separate_invariant_violations(no). | |
712 | ||
713 | % Either no or yes. The former means we don't view every outgoing transition for | |
714 | % each equivalence class. The latter means we do. | |
715 | signature_merge_show_all_transitions(yes). | |
716 | ||
717 | set_signature_merge_show_all_transitions(H) :- | |
718 | (signature_merge_show_all_transitions(H) -> | |
719 | true | |
720 | ; retractall(signature_merge_show_all_transitions(_)), | |
721 | assertz(signature_merge_show_all_transitions(H))). | |
722 | ||
723 | %state_merge_separate_iviolations(V) :- | |
724 | % (((V == yes) ; (V == no)) -> | |
725 | % retractall(state_merge_separate_invariant_violations(_)), | |
726 | % assertz(state_merge_separate_invariant_violations(V)) | |
727 | % ; print('invalid state_merge_separate_iviolations/1 mode: '),print(V), | |
728 | % print(' should be either, yes, or no'),nl). | |
729 | ||
730 | % Either normal ==> perform reduction on original state space | |
731 | % OR nfa_to_dfa ==> perform reduction on state space outputted from nfa_to_dfa function | |
732 | % NB: SPELL YOUR OPTION CORRECTLY OR GET WEIRD RESULTS! | |
733 | %state_merge_filter_mode(normal). | |
734 | ||
735 | % clean up the database | |
736 | cleanup_state_merge :- | |
737 | retractall(state_signature(_,_,_,_)),retractall(state_merge_equivalence_class(_,_,_)), | |
738 | retractall(counter(_)), | |
739 | retractall(non_det_trans(_,_,_)), | |
740 | retractall(non_def_trans(_,_)) | |
741 | /*,retractall(desired_user_transition(_,_))*/. | |
742 | ||
743 | % get the transitions: take care that we regard the different modes | |
744 | state_merge_transition_filter(A,B,Sig,D) :- | |
745 | transition(A,B,D),transition_info(B,_,_,Sig), | |
746 | desired_user_transition(signature_merge,Sig). | |
747 | ||
748 | % get the states: take care that we regard the different modes | |
749 | state_merge_state_filter(A,B,B) :- visited_expression(A,B). | |
750 | ||
751 | % assert the transitions stemming from each state | |
752 | assert_state_merges :- | |
753 | state_merge_state_filter(ID,State,_), | |
754 | compute_signature_for_state(ID,State,Res), | |
755 | % check whether to separate each invariant violation in it's own | |
756 | % equivalence class. | |
757 | (state_merge_separate_invariant_violations(no) -> | |
758 | assert_state_signature(ID,Res,ok) | |
759 | ; (invariant_violated(ID) -> | |
760 | assert_state_signature(ID,Res,invariant_violated) | |
761 | ; assert_state_signature(ID,Res,ok))), | |
762 | fail. | |
763 | assert_state_merges. | |
764 | ||
765 | assert_state_signature(ID,Signature,OK) :- | |
766 | (state_merge_equivalence_class(Signature,ClassID,OK) | |
767 | -> true | |
768 | ; increment_counter(ClassID), | |
769 | assertz(state_merge_equivalence_class(Signature,ClassID,OK)) | |
770 | ), | |
771 | assertz(state_signature(ID,Signature,OK,ClassID)). | |
772 | ||
773 | %:- dynamic observing_variable/1. | |
774 | /* set this predicate if we want to observe a particular variable instead of the signature */ | |
775 | %observing_variable('display_st'). | |
776 | ||
777 | %compute_signature_for_state(_,State,Res) :- | |
778 | % observing_variable(Var),store:lookup_value(Var,State,Val),!, Res=Val. | |
779 | compute_signature_for_state(ID,_,Res) :- | |
780 | findall(Tr,state_merge_transition_filter(ID,_,Tr,_),Res_), | |
781 | sort(Res_,Res). | |
782 | ||
783 | % find out the state equivalence class for a given state | |
784 | find_state_merge_equivalence_class_for_state(State,ClassID) :- | |
785 | state_signature(State,_TransSig,_Status,ClassID),!. | |
786 | ||
787 | % find out a state corresponding to a given equivalence class | |
788 | find_state_for_state_merge_equivalence_class(ClassID,State) :- | |
789 | state_signature(State,_SIG,_OK,ClassID). | |
790 | ||
791 | % apply the state merge algorithm | |
792 | state_merge :- | |
793 | cleanup_state_merge, | |
794 | % work on the correct state space e.g. the original one as opposed to | |
795 | % one outputted by another algorithm | |
796 | /*assert_desired_user_transitions,*/ | |
797 | print_message('Computing Signatures'), | |
798 | assert_state_merges. | |
799 | ||
800 | % the state merge state's colour | |
801 | state_merge_state_colour(State, Colour) :- | |
802 | (state_merge_equivalence_class([],State,_) -> | |
803 | get_preference(dot_open_node_colour,Colour) | |
804 | ; get_preference(dot_normal_node_colour,Colour)). | |
805 | ||
806 | % print a state's id for the state merge - print 'root' for this if the id maps to the root | |
807 | print_state_merge_state_id(State) :- | |
808 | (find_state_merge_equivalence_class_for_state(root,State) -> print(root) | |
809 | ; print(State)). | |
810 | ||
811 | % print a transition for dot | |
812 | print_transition_for_dot(A,IndividualSig,C) :- Trans = [A,IndividualSig,C], | |
813 | (transition_printed(Trans) -> | |
814 | true | |
815 | ; assertz(transition_printed(Trans)), | |
816 | print_state_merge_state_id(A),print('->'),print(C), | |
817 | (find_state_merge_equivalence_class_for_state(root, A) -> | |
818 | /* Style = 'solid', Color='blue' % dealing with the root */ | |
819 | (non_det_trans(A,IndividualSig,true) -> % not dealing with root | |
820 | Style = 'dotted' % there's more than one of these transitions | |
821 | ; Style = 'solid'), | |
822 | (non_def_trans(A,IndividualSig) -> | |
823 | Color = 'purple';Color = 'black') | |
824 | ; (non_det_trans(A,IndividualSig,true) -> % not dealing with root | |
825 | Style = 'dotted' % there's more than one of these transitions | |
826 | ; Style = 'solid'), | |
827 | (non_def_trans(A,IndividualSig) -> | |
828 | Color = 'purple';Color = 'blue')), | |
829 | print('[style='),print(Style),print(', color=\"'),print(Color), | |
830 | print('\", label=\"'),print(IndividualSig),print('\"];'),nl). | |
831 | ||
832 | % print a state for dot | |
833 | print_class_as_dotstate(ClassID,OK) :- | |
834 | (state_printed(ClassID) -> | |
835 | true | |
836 | ; assertz(state_printed(ClassID)), | |
837 | state_merge_state_colour(ClassID, StateColour), | |
838 | get_preference(dot_invariant_violated_node_colour,IColor), | |
839 | print_state_merge_state_id(ClassID), | |
840 | print(' ['), | |
841 | (find_state_merge_equivalence_class_for_state(root,ClassID) -> | |
842 | print('shape=invtriangle, color=green, '), | |
843 | print('label=\"\", id=\"'),print(root) | |
844 | ; ((OK = invariant_violated) -> | |
845 | print('shape=ellipse, style=filled, fillcolor="'), | |
846 | print(IColor),print('\", label=\"'), | |
847 | print_node_label(ClassID),print('\" , id=\"'),print_node_label(ClassID) | |
848 | ; print('shape=ellipse, color=\"'),print(StateColour),print('\", label=\"'), | |
849 | print_node_label(ClassID),print('\" , id=\"'),print_node_label(ClassID))), | |
850 | print('\"];'),nl). | |
851 | ||
852 | print_node_label(ClassID) :- | |
853 | (preference(dot_print_node_ids,false) | |
854 | -> state_merge_equivalence_class(Signature,ClassID,_OK), | |
855 | print(Signature) | |
856 | ; preference(dot_print_node_info,true) | |
857 | -> state_merge_equivalence_class(Signature,ClassID,_OK), | |
858 | print(ClassID),print('\\n'), print(Signature) | |
859 | ; print(ClassID) | |
860 | ). | |
861 | ||
862 | % print the merged states | |
863 | print_state_merge_states_for_dot :- | |
864 | state_merge_equivalence_class(_,ClassID,OK), | |
865 | print_class_as_dotstate(ClassID,OK),fail. | |
866 | print_state_merge_states_for_dot. | |
867 | ||
868 | % assert whether there is more than one transition from an equivalence class | |
869 | % with the same label. | |
870 | assert_non_det_trans(_State,StateID,IndividualTransSignature,_To1ID) :- | |
871 | non_det_trans(StateID,IndividualTransSignature,_Status),!. % already computed | |
872 | assert_non_det_trans(State,StateID,TransSignature,To1ID) :- | |
873 | find_state_for_state_merge_equivalence_class(StateID,AnotherState), | |
874 | State \= AnotherState, | |
875 | \+ not_all_transitions_added(AnotherState), % this is an open node; not yet computed | |
876 | /* state_merge_transition_filter */ | |
877 | transition(AnotherState,_,TransSignature,To2), | |
878 | \+(find_state_merge_equivalence_class_for_state(To2,To1ID)),!, | |
879 | %print_message(non_det(StateID,TransSignature,To1ID,AnotherState,To2)), | |
880 | asserta(non_det_trans(StateID,TransSignature,true)). | |
881 | assert_non_det_trans(_State,StateID,IndividualTransSignature,_To1ID) :- | |
882 | assertz(non_det_trans(StateID,IndividualTransSignature,false)). | |
883 | ||
884 | % print the transitions of the merged states. | |
885 | print_state_merge_transitions_for_dot :- | |
886 | transition(State,_,IndividualSig,To), | |
887 | find_state_merge_equivalence_class_for_state(State,ID1), | |
888 | find_state_merge_equivalence_class_for_state(To,ID2), | |
889 | assert_non_det_trans(State,ID1,IndividualSig,ID2), | |
890 | (desired_user_transition(signature_merge,IndividualSig) -> | |
891 | print_transition_for_dot(ID1,IndividualSig,ID2) | |
892 | ; (signature_merge_show_all_transitions(yes) -> | |
893 | (non_def_trans(ID1,IndividualSig) -> | |
894 | true | |
895 | ; assertz(non_def_trans(ID1,IndividualSig))), | |
896 | print_transition_for_dot(ID1,IndividualSig,ID2) | |
897 | ; true) | |
898 | ), | |
899 | fail. | |
900 | print_state_merge_transitions_for_dot. | |
901 | ||
902 | % print the merged state graph for dot | |
903 | print_state_merge_for_dot :- | |
904 | % check if user has specified all transitions | |
905 | (use_all_operations(signature_merge,yes) -> | |
906 | set_use_all_operations(signature_merge,yes) | |
907 | ; true), | |
908 | clean_up_after_printing, | |
909 | print_message('Performing State Merge'), | |
910 | state_merge, | |
911 | print_graph_header(state_merge), | |
912 | print_message('Generating Dot States'), | |
913 | print_state_merge_states_for_dot, | |
914 | print_message('Generating Dot Transitions'), | |
915 | print_state_merge_transitions_for_dot, | |
916 | print_graph_footer. | |
917 | ||
918 | print_state_merge_for_dot(File) :- print_state_merge_for_dot2(File). | |
919 | print_state_merge_for_dot2(File) :- | |
920 | tell(File),print_state_merge_for_dot,told. | |
921 | ||
922 | ||
923 | % For ProB 2.0 Graphical Visualizations | |
924 | extract_graph_of_state_merge(Graph) :- | |
925 | % check if user has specified all transitions | |
926 | (use_all_operations(signature_merge,yes) -> | |
927 | set_use_all_operations(signature_merge,yes) | |
928 | ; true), | |
929 | clean_up_after_printing, | |
930 | print_message('Performing State Merge'), | |
931 | state_merge, | |
932 | print_message('Generating States'), | |
933 | extract_state_merge_states(States), | |
934 | print_message('Generating Transitions'), | |
935 | extract_state_merge_transitions(Transitions), | |
936 | Graph = [States,Transitions]. | |
937 | ||
938 | extract_state_merge_states(States) :- | |
939 | findall(State,extract_state_for_graph(State),States). | |
940 | ||
941 | extract_state_for_graph(State) :- | |
942 | state_merge_equivalence_class(Sig,Id,OK), | |
943 | State = state(Sig,Id,OK). | |
944 | ||
945 | extract_state_merge_transitions(Transitions) :- | |
946 | findall(Trans,extract_state_merge_transition(Trans),Transitions). | |
947 | ||
948 | extract_state_merge_transition(Transition) :- | |
949 | transition(State,_,IndividualSig,To), | |
950 | find_state_merge_equivalence_class_for_state(State,ID1), | |
951 | find_state_merge_equivalence_class_for_state(To,ID2), | |
952 | assert_non_det_trans(State,ID1,IndividualSig,ID2), | |
953 | (desired_user_transition(signature_merge,IndividualSig) -> | |
954 | extract_transition_representation(ID1,IndividualSig,ID2,Color,Style) | |
955 | ; (signature_merge_show_all_transitions(yes) -> | |
956 | (non_def_trans(ID1,IndividualSig) -> | |
957 | true | |
958 | ; assertz(non_def_trans(ID1,IndividualSig))), | |
959 | extract_transition_representation(ID1,IndividualSig,ID2,Color,Style) | |
960 | ; true) | |
961 | ), | |
962 | Transition = trans(ID1,IndividualSig,ID2,Color,Style). | |
963 | ||
964 | ||
965 | extract_transition_representation(A,IndividualSig,C,Color,Style) :- | |
966 | Trans = [A,IndividualSig,C], | |
967 | (transition_printed(Trans) -> | |
968 | fail | |
969 | ; assertz(transition_printed(Trans)), | |
970 | (find_state_merge_equivalence_class_for_state(root, A) -> | |
971 | /* Style = 'solid', Color='blue' % dealing with the root */ | |
972 | (non_det_trans(A,IndividualSig,true) -> % not dealing with root | |
973 | Style = 'dotted' % there's more than one of these transitions | |
974 | ; Style = 'solid'), | |
975 | (non_def_trans(A,IndividualSig) -> | |
976 | Color = 'purple';Color = 'black') | |
977 | ; (non_det_trans(A,IndividualSig,true) -> % not dealing with root | |
978 | Style = 'dotted' % there's more than one of these transitions | |
979 | ; Style = 'solid'), | |
980 | (non_def_trans(A,IndividualSig) -> | |
981 | Color = 'purple';Color = 'blue')) | |
982 | ). | |
983 | ||
984 | ||
985 | ||
986 | % clean up the database from assertions used during graph printing. | |
987 | clean_up_after_printing :- | |
988 | retractall(state_printed(_)),retractall(transition_printed(_)). | |
989 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
990 | End of signature merge reduction | |
991 | ==============================================================================*/ | |
992 | ||
993 | ||
994 | :- volatile things/1. | |
995 | :- dynamic things/1. | |
996 | assert_associated_nodes([]). | |
997 | assert_associated_nodes([H|T]) :- | |
998 | (things(H) -> | |
999 | assert_associated_nodes(T) | |
1000 | ; assertz(things(H)),findall(X,transition(X,_,H),Res), | |
1001 | append(T,Res,Ress),assert_associated_nodes(Ress)). | |
1002 | ||
1003 | check_against_list(_,[]). | |
1004 | check_against_list(Element,[H|T]) :- | |
1005 | (state_printed(Element) -> | |
1006 | true | |
1007 | ; assertz(state_printed(Element)), | |
1008 | visualize_graph:tcltk_print_node_for_dot(Element)), | |
1009 | /* (transition_printed([Element,H]) -> | |
1010 | true | |
1011 | ; assertz(transition_printed([Element,H])), */ | |
1012 | (transition(Element,_,H) -> visualize_graph:tcltk_print_transitions_for_dot(Element,H) | |
1013 | ; true), | |
1014 | check_against_list(Element,T). | |
1015 | ||
1016 | print_dot_from_node_list(_,File) :- | |
1017 | tell(File), | |
1018 | retractall(state_printed(_)), | |
1019 | retractall(transition_printed(_)), | |
1020 | print_graph_header(general_graph),fail. | |
1021 | print_dot_from_node_list(Llist,_) :- | |
1022 | sort(Llist,List), | |
1023 | member(N1,List), | |
1024 | check_against_list(N1,List),fail. | |
1025 | print_dot_from_node_list(_,_) :- | |
1026 | print_graph_footer,told. | |
1027 | ||
1028 | print_subgraph_associated_with_node(Node,File) :- | |
1029 | retractall(things(_)), | |
1030 | (is_list(Node) -> | |
1031 | assert_associated_nodes(Node) | |
1032 | ; assert_associated_nodes([Node])), | |
1033 | findall(X,reduce_graph_state_space:things(X),Result), | |
1034 | print_dot_from_node_list(Result,File). | |
1035 | ||
1036 | print_subgraph_associated_with_invariant_violations(File) :- | |
1037 | findall(X,invariant_violated(X),R),remove_dups(R,Rr), | |
1038 | print_subgraph_associated_with_node(Rr,File). | |
1039 | ||
1040 | print_subgraph_of_goal_nodes(File) :- | |
1041 | assert_goal_nodes,goal_nodes(L),print_dot_from_node_list(L,File). | |
1042 | ||
1043 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
1044 | End of Iterative Deepening search for a node | |
1045 | ==============================================================================*/ | |
1046 | ||
1047 | ||
1048 | ||
1049 | ||
1050 | ||
1051 |