| 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 ***/ |