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