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