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