| 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 | /* originally graph_canon.pl - graph isomorphism module 1/11/2005 */ | |
| 6 | ||
| 7 | :- module(state_graph_canon, [ | |
| 8 | state_graph/2, | |
| 9 | state_graph_for_dot/2 | |
| 10 | ]). | |
| 11 | ||
| 12 | ||
| 13 | :- use_module(probsrc(error_manager)). | |
| 14 | ||
| 15 | %:- compile(sp4_compatibility). | |
| 16 | :- use_module(probsrc(tools)). | |
| 17 | ||
| 18 | :- use_module(probsrc(module_information),[module_info/2]). | |
| 19 | :- module_info(group,symmetry). | |
| 20 | :- module_info(description,'Compute canonical forms of state as graphs.'). | |
| 21 | ||
| 22 | ||
| 23 | /* given the properties clause of the bmachine, extract the names of the | |
| 24 | known constants, in the order of their declaration */ | |
| 25 | % currently works only for machines that do not use inheritance | |
| 26 | ||
| 27 | % removed | |
| 28 | ||
| 29 | % ------------------------------------------------------------------------------ | |
| 30 | ||
| 31 | /* Assert all the elements of the sets used by this machine, for this session of ProB, | |
| 32 | and also those that are symmetric or not */ | |
| 33 | % assert_global_set_info :- better use b_global_sets now | |
| 34 | ||
| 35 | /******************************************************************************/ | |
| 36 | ||
| 37 | ||
| 38 | ||
| 39 | /******** STATE AS SINGLE GRAPH ***************/ | |
| 40 | :- use_module(probsrc(specfile),[expand_const_and_vars/3]). | |
| 41 | ||
| 42 | :- dynamic generate_state_for_dot/0. | |
| 43 | ||
| 44 | state_graph_for_dot(State,R) :- | |
| 45 | assertz(generate_state_for_dot), | |
| 46 | call_cleanup(state_graph(State,R), retract(generate_state_for_dot)). | |
| 47 | ||
| 48 | state_graph(root,R) :- !,R=[]. | |
| 49 | state_graph(concrete_constants(C),SG) :- !, state_graph(C,SG). | |
| 50 | state_graph(const_and_vars(ConstID,S),SG) :- !, | |
| 51 | expand_const_and_vars(ConstID,S,Full), | |
| 52 | reset_n, | |
| 53 | state_graph(Full,[],SG). | |
| 54 | state_graph(S,SG) :- | |
| 55 | reset_n, | |
| 56 | state_graph(S,[],SG). | |
| 57 | state_graph([],Final,Final). | |
| 58 | state_graph([bind(VarName,Value)|T],C,F) :- | |
| 59 | tools:string_escape(VarName,EscVarName), | |
| 60 | (top_level_translate_node(Value,EscVarName,PG) -> true | |
| 61 | %print(translate(Value,VarName,PG)),nl | |
| 62 | ; add_error(state_graph,'Call failed: ',top_level_translate_node(Value,VarName,PG))), | |
| 63 | append(PG,C,C2), | |
| 64 | state_graph(T,C2,F). | |
| 65 | ||
| 66 | top_level_translate_node(DataValue,VariableName,NewTransitions) :- | |
| 67 | % Input: DataValue | |
| 68 | % Output: NewTransitions required to represent the structure | |
| 69 | % In contrast to translate_inner_node, we do not generate new inner artificial nodes; | |
| 70 | % VariableName is used as an arc-label to encode the top-level information | |
| 71 | expand_data_value(DataValue,S1), | |
| 72 | % check whether S1 is a relation/atom/set | |
| 73 | ( relation(S1) -> gen_relation(S1,VariableName,NewTransitions) | |
| 74 | ; emptyset(S1) -> NewTransitions = [(sg_root,VariableName,sg_root)] | |
| 75 | %% [('$Empty_Set',VariableName,sg_root)] has problems as we cannot distinguish between v={} and v={{}} | |
| 76 | %% [] has problems when we have set_up_constants and variables which are all initialised with | |
| 77 | %% the empty set: then we get confused between set_up_constants & initialise_machine | |
| 78 | ; list_set(S1) -> gset(S1,sg_root,VariableName,[],NewTransitions) | |
| 79 | ; S1=(P1,P2) -> % a pair | |
| 80 | translate_inner_node(P1,Node1,NewTrans1), | |
| 81 | translate_inner_node(P2,Node2,NewTrans2), | |
| 82 | append([(Node1,VariableName,Node2)|NewTrans1],NewTrans2,NewTransitions) | |
| 83 | ; % an atom | |
| 84 | NewTransitions = [(DataValue,VariableName,sg_root)] | |
| 85 | ). | |
| 86 | ||
| 87 | :- use_module(probsrc(custom_explicit_sets),[is_custom_explicit_set/2, try_expand_custom_set_with_catch/3]). | |
| 88 | expand_data_value(DataValue,S1) :- | |
| 89 | (is_custom_explicit_set(DataValue,expand_data_value) | |
| 90 | -> try_expand_custom_set_with_catch(DataValue,S1,expand_data_value) | |
| 91 | ; DataValue=S1 | |
| 92 | ). | |
| 93 | % TO DO: convert to Difference-List to get rid of all the appends below | |
| 94 | ||
| 95 | ||
| 96 | gset([],_,_,F,F). | |
| 97 | gset([S1|T],Parent,TransitionLabel,F,Fi) :- | |
| 98 | translate_inner_node(S1,C1,S1_NewTransitions), | |
| 99 | append([(C1,TransitionLabel,Parent)|S1_NewTransitions],F,F4), | |
| 100 | gset(T,Parent,TransitionLabel,F4,Fi). | |
| 101 | ||
| 102 | gen_relation(S1,VariableName,NewTransitions) :- | |
| 103 | (S1=[H|_],is_pair_to_pair(H) -> MatchTriples=false ; MatchTriples=true), | |
| 104 | grelation(S1,MatchTriples,sg_root,VariableName,[],NewTransitions). | |
| 105 | ||
| 106 | :- use_module(probsrc(kernel_objects),[infer_value_type/2]). | |
| 107 | is_pair_to_pair(((A,B),(C,D))) :- | |
| 108 | infer_value_type(A,T1), infer_value_type(C,T1), | |
| 109 | infer_value_type(B,T2), infer_value_type(D,T2). | |
| 110 | ||
| 111 | :- use_module(library(lists),[append/2]). | |
| 112 | :- use_module(probsrc(translate),[translate_bvalue_for_dot/2]). | |
| 113 | grelation([],_,_,_,F,F). | |
| 114 | grelation([(E0,E1,E2)|T],true,Parent,VariableName,F,Fi) :- | |
| 115 | % interpret E0 as label for string; note: (E0,E1,E2) matches ((A,B),(C,D)) | |
| 116 | generate_state_for_dot, | |
| 117 | simple_value(E0), | |
| 118 | !, | |
| 119 | translate_inner_node(E1,C1,E1_NewTransitions), | |
| 120 | translate_inner_node(E2,C2,E2_NewTransitions), | |
| 121 | translate_bvalue_for_dot(E0,E0String), | |
| 122 | Label =.. [VariableName,E0String], | |
| 123 | append([E2_NewTransitions,[(C1,Label,C2)|E1_NewTransitions],F],F6), | |
| 124 | grelation(T,true,Parent,VariableName,F6,Fi). | |
| 125 | grelation([(E1,E2)|T],MatchTriples,Parent,VariableName,F,Fi) :- | |
| 126 | translate_inner_node(E1,C1,E1_NewTransitions), | |
| 127 | translate_inner_node(E2,C2,E2_NewTransitions), | |
| 128 | append([E2_NewTransitions,[(C1,VariableName,C2)|E1_NewTransitions],F],F6), | |
| 129 | grelation(T,MatchTriples,Parent,VariableName,F6,Fi). | |
| 130 | ||
| 131 | ||
| 132 | ||
| 133 | :- use_module(probsrc(preferences),[get_preference/2]). | |
| 134 | ||
| 135 | translate_inner_node(DataValue,NodeinGraph,NewTransitions) :- %% print(trans(DataValue)),nl, | |
| 136 | % Input: DataValue | |
| 137 | % Output: NodeinGraph representing DataValue + NewTransitions required to represent the structure | |
| 138 | (get_preference(dot_state_graph_decompose,false) -> % treat as an atom | |
| 139 | NewTransitions = [], NodeinGraph = DataValue | |
| 140 | ; expand_data_value(DataValue,S1), | |
| 141 | % check whether S1 is a atom/set | |
| 142 | ( emptyset(S1) -> NodeinGraph = [], %'$Empty_Set', | |
| 143 | NewTransitions=[] | |
| 144 | ; cn_value(S1,NodeinGraph) -> NewTransitions=[] | |
| 145 | ; simple_pair_value(S1),generate_state_for_dot -> NodeinGraph=S1, NewTransitions=[] | |
| 146 | ; list_set(S1) -> | |
| 147 | new_sg_node(S1,NodeinGraph), | |
| 148 | gset(S1,NodeinGraph,'$to_el',[],NewTransitions) % use the $to_el label to encode membership, using ':' would be more readable; but less efficient for symmetry (extra layers required) | |
| 149 | ; S1=(P1,P2) | |
| 150 | -> % a complex pair | |
| 151 | translate_inner_node(P1,Node1,NewTrans1), | |
| 152 | translate_inner_node(P2,Node2,NewTrans2), | |
| 153 | new_sg_node(S1,NodeinGraph), | |
| 154 | append([(NodeinGraph,'$from',Node1), | |
| 155 | (NodeinGraph,'$to_el',Node2)|NewTrans1],NewTrans2,NewTransitions) | |
| 156 | ; % an atom | |
| 157 | NewTransitions = [], | |
| 158 | NodeinGraph = S1 | |
| 159 | ) | |
| 160 | ). | |
| 161 | ||
| 162 | % simple values which can easily be rendered like a record | |
| 163 | simple_pair_value((A,B)) :- !,simple_pair_value(A), simple_pair_value(B). | |
| 164 | simple_pair_value(X) :- simple_value(X). | |
| 165 | ||
| 166 | relation([(_,_)|_]). | |
| 167 | relation([(X,_,_)|_]) :- generate_state_for_dot, simple_value(X). % TO DO: check if it is a function ?; maybe also support other nested ternary relations | |
| 168 | emptyset([]). | |
| 169 | list_set([]). | |
| 170 | list_set([_|_]). | |
| 171 | % what about AVL : supposed expanded before | |
| 172 | ||
| 173 | ||
| 174 | simple_value(X) :- atomic(X). | |
| 175 | simple_value(int(_)). | |
| 176 | simple_value(fd(_,_)). | |
| 177 | simple_value(string(_)). | |
| 178 | simple_value(pred_false). | |
| 179 | simple_value(pred_true). | |
| 180 | ||
| 181 | :- dynamic cn/1, cn_value/2. | |
| 182 | ||
| 183 | reset_n :- | |
| 184 | retractall(cn(_)),retractall(cn_value(_,_)),asserta(cn(0)). | |
| 185 | ||
| 186 | /* generate a new state graph node */ | |
| 187 | new_sg_node(Value,abs_cn(J,NestingCol)) :- retract(cn(I)), J is I+1, | |
| 188 | asserta(cn(J)), | |
| 189 | get_abstract_node_type_colour(Value,NestingCol), | |
| 190 | assertz(cn_value(Value,abs_cn(J,NestingCol))). | |
| 191 | ||
| 192 | % for the moment: just compute the "depth/nesting" of the relation; we should do something more intelligent | |
| 193 | get_abstract_node_type_colour([],C) :- !,C=1. | |
| 194 | get_abstract_node_type_colour([H|_],C) :- !, get_abstract_node_type_colour(H,CH), | |
| 195 | C is CH+1. | |
| 196 | get_abstract_node_type_colour((A,_),C) :- !, get_abstract_node_type_colour(A,CH), | |
| 197 | C is CH+1. | |
| 198 | get_abstract_node_type_colour(_,0). | |
| 199 | ||
| 200 | ||
| 201 | ||
| 202 | /**********************************************/ | |
| 203 | ||
| 204 | /****** CANONICAL LABELLING USING LEXICOGRAPHIC AND AUTOMORHISM PRUNING: ******* | |
| 205 | ****** Makes use of Schreier-Sims data structure defined in Kreher's ******* | |
| 206 | ****** C.A.G.E.S book, and uses the techniques for pruning described in ******* | |
| 207 | ****** the chapter 7. *******/ | |
| 208 | ||
| 209 | ||
| 210 | ||
| 211 | /******************************************************************* | |
| 212 | */ | |
| 213 | % schreiersims.pl implementation of the schreier sims representation of an | |
| 214 | % automorphism group taken from Kreher's Combinatorial Algorithms book. Provides | |
| 215 | % method for membership test of a permutation in the group that should run in O(n^2) | |
| 216 | % time. Plans are not to spend time on implementing a method for listing all permutations | |
| 217 | % represented in the group -- since this is not required at the time of writing. | |
| 218 | ||
| 219 | % THIS IS NOT CURRENTLY USED, BUT WAS ONCE USED TO MORE CLOSELY FOLLOW NAUTY. | |
| 220 | % HOWEVER, USING SCHREIERSIMS STRUCTURES IMPLEMENTATION IS VERY LIST INTENSIVE | |
| 221 | % RESULTING IN A POOR PERFORMANCE -- HENCE NOT USED NOW. FUTURE WORK MAY WANT | |
| 222 | % TO LOOK AT ITS USE BELOW.. | |
| 223 | ||
| 224 | ||
| 225 | /*** END OF CANONICAL LABELLING USING LEXICOGRAPHIC AND AUTOMORPHISM PRUNING ***/ |