1 % (c) 2006-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
6 :- module(symmetry_marker,[precompile_marker_typing_info/0,
7 compute_marker_for_state/2,
8 type_allows_for_precise_marker/1,
9 hash_model_checking_imprecise/2,
10 non_symmetric_type/1]).
11 /* File: symmetry_marker.pl */
12 /* Created: 28/9/06 */
13
14
15 :- use_module(module_information).
16 :- module_info(group,symmetry).
17 :- module_info(description,'This module computes symmetry hash markers for B states (used for hash symmetry).').
18
19 :- use_module(debug).
20
21 :- use_module(library(lists)).
22
23 /* marker(Object, Type, ValueAsTerm) */
24
25
26 :- use_module(b_global_sets,[is_unused_b_global_constant/3,
27 b_global_deferred_set_with_card_gt1/1, b_global_set_with_potential_symmetry/1]).
28
29 :- use_module(error_manager).
30
31 type_allows_for_precise_marker(integer).
32 type_allows_for_precise_marker(string).
33 type_allows_for_precise_marker(boolean).
34 type_allows_for_precise_marker(set(global(_))) :- !. % ok, whether global set deferred or not
35 type_allows_for_precise_marker(set(couple(X,Y))) :- !,
36 ((Y=global(_),non_symmetric_type(X)) % type_allows_for_precise_marker(Y) not ok; unless we are guaranteed to have a function
37 -> true
38 ; (X=global(_),non_symmetric_type(Y)) % see comment above
39 -> true
40 ; (non_symmetric_type(X), non_symmetric_type(Y)) -> true
41 ; print_fail(set(couple(X,Y)))).
42 type_allows_for_precise_marker(set(X)) :-
43 (non_symmetric_type(X) -> true
44 ; print_fail(set(X))).
45 type_allows_for_precise_marker(seq(X)) :- !, type_allows_for_precise_marker(set(couple(integer,X))).
46 type_allows_for_precise_marker(global(_)).
47 type_allows_for_precise_marker(couple(X,Y)) :- !,type_allows_for_precise_marker(X),
48 type_allows_for_precise_marker(Y).
49 type_allows_for_precise_marker(record(F)) :- !, fields_allows_for_precise_marker(F).
50
51 fields_allows_for_precise_marker([]).
52 fields_allows_for_precise_marker([field(_Name,Type)|T]) :-
53 type_allows_for_precise_marker(Type),
54 fields_allows_for_precise_marker(T).
55
56 :- use_module(preferences,[get_preference/2]).
57 hash_model_checking_imprecise(ID,BasicType) :- %print(check),nl,
58 get_preference(symmetry_mode,hash),
59 (b_get_machine_constants(TL) ; b_get_machine_variables(TL)),
60 member(b(identifier(ID),BasicType,_),TL),
61 \+ type_allows_for_precise_marker(BasicType).
62
63
64 %:- use_module(translate,[pretty_type/2]).
65 print_fail(_T) :-
66 %print('*** type may lead to imprecise hash marker:'),
67 %translate:pretty_type(T,S),print(S),nl,
68 fail.
69 % a type which does not contain any deferred set elements
70 non_symmetric_type(integer) :- !.
71 non_symmetric_type(boolean) :- !.
72 non_symmetric_type(string) :- !.
73 non_symmetric_type(set(X)) :- !, non_symmetric_type(X).
74 non_symmetric_type(couple(X,Y)) :- !,non_symmetric_type(X), non_symmetric_type(Y).
75 ?non_symmetric_type(global(X)) :- !, \+ b_global_set_with_potential_symmetry(X).
76 non_symmetric_type(seq(X)) :- !, non_symmetric_type(X).
77 non_symmetric_type(record(F)) :- !, non_symmetric_fields(F).
78 non_symmetric_type(freetype(_)) :- !.
79 non_symmetric_type(X) :- add_error_fail(non_symmetric_type,'Unknown type: ',X).
80
81 non_symmetric_fields([]).
82 non_symmetric_fields([field(_Name,Type)|T]) :- non_symmetric_type(Type),
83 non_symmetric_fields(T).
84
85 /* first a predicate to count the occurrences of anonymous set elements */
86
87 /* possible improvement: count nr of occurences for each variable */
88
89 :- use_module(bmachine,[b_get_machine_constants/1, b_get_machine_variables/1]).
90 :- volatile non_symmetric_var/2.
91 :- dynamic non_symmetric_var/2.
92 precompile_marker_typing_info :-
93 retractall(non_symmetric_var(_,_)),
94 bmachine:b_get_machine_variables(TypedVarList),
95 bmachine:b_get_machine_constants(TypedCstList),
96 append(TypedCstList,TypedVarList,List),
97 analyse_typed_varlist(List).
98
99 analyse_typed_varlist([]).
100 analyse_typed_varlist([b(identifier(ID),Type,_)|T]) :-
101 (non_symmetric_type(Type)
102 -> assert(non_symmetric_var(ID,Type))
103 %% ,print(non_sym(ID,Type)),nl
104 ; true),
105 analyse_typed_varlist(T).
106
107
108 %:- dynamic count/4.
109
110
111 count_state(State) :- empty_avl(E),
112 %nl,translate:print_bstate(State),nl,
113 count_env(State,E,Out),
114 %portray_avl(Out),nl,
115 compute_deferred_set_el_markers(Out).
116
117 count_env([],I,O) :- !, O=I.
118 count_env([bind(Var,Val)|T],In,Out) :- !,
119 (non_symmetric_var(Var,_) -> In=I1
120 ; %print(counting(Var)),nl,
121 count_obj(Val,Var,In,I1)),
122 count_env(T,I1,Out).
123 count_env(E,I,O) :- add_error(symmetry_marker,'Illegal env: ',E), O=I.
124
125 :- use_module(library(avl)).
126 /* IDEA: instead of just storing variable Var, store entire path to
127 occurrence of fd(Nr,T) */
128 count_obj(fd(Nr,T),PathToObject,In,Out) :- !, %print(fd(Nr,T)),
129 ((b_global_deferred_set_with_card_gt1(T) ;
130 is_unused_b_global_constant(T,Nr,_) /* TO DO: only makes sense if more than one unused global constant */
131 )
132 -> %add_count(PathToObject,Nr,T,In,Out)
133 add_avl_count(In,usage_count(Nr,T,PathToObject),Out)
134 ; Out=In
135 ).
136 count_obj((X,Y),PathToObject,In,Out) :- !, count_pair(X,Y,PathToObject,In,Out).
137 count_obj([X|Y],PathToObject,In,Out) :- !,
138 (could_contain_deferred_set_elements(X)
139 -> count_obj_list([X|Y],PathToObject,In,Out) /* only count set if there are deferred set elements inside */
140 ; Out=In
141 ).
142 count_obj(avl_set(A),PathToObject,In,Out) :- !,
143 (avl_could_contain_deferred_set_element(A)
144 -> count_avl(A,el(PathToObject),In,Out)
145 ; Out=In
146 ).
147 %count_obj([],_,O,O).
148 count_obj(_,_,O,O).
149 % no need to have base case for empty avl; as empty set represented by []
150
151
152 count_avl(empty,_PathToObject) --> [].
153 count_avl(node(Key,_True,_,L,R),PathToObject) -->
154 count_avl(L,PathToObject), count_obj(Key,PathToObject),
155 count_avl(R,PathToObject).
156
157 count_pair(X,Y,PathToObject,In,Out) :- not_symmetric(Y),!,
158 count_obj(X,mapped_to(PathToObject,Y),In,Out).
159 count_pair(X,Y,PathToObject,In,Out) :- not_symmetric(X),!,
160 count_obj(Y,mapped_from(X,PathToObject),In,Out).
161 count_pair(X,X,PathToObject,In,Out) :- !, /* is this correct ??
162 should we only do it for simple X, what about sets: should
163 work if normalised ?? */
164 count_obj(X,leftright(PathToObject),In,Out).
165 count_pair(X,Y,PathToObject,In,Out) :- !,
166 count_obj(X,left(PathToObject),In,I1),
167 count_obj(Y,right(PathToObject),I1,Out).
168
169
170 could_contain_deferred_set_elements([]).
171 could_contain_deferred_set_elements([H|_]) :- could_contain_deferred_set_elements(H).
172 could_contain_deferred_set_elements(fd(_Nr,_T)).
173 %b_global_deferred_set_with_card_gt1(T) ; b_global_sets:is_unused_b_global_constant(T,Nr,_).
174 could_contain_deferred_set_elements((A,B)) :-
175 (could_contain_deferred_set_elements(A) -> true ; could_contain_deferred_set_elements(B)).
176 could_contain_deferred_set_elements(avl_set(A)) :- avl_could_contain_deferred_set_element(A).
177
178 avl_could_contain_deferred_set_element(node(X,_,_,_L,_R)) :- /* first underscore should be true */
179 could_contain_deferred_set_elements(X).
180 %an_avl_element(node(X,_,_,_L,_R),X). /* first underscore should be true */
181
182 count_obj_list([],_,O,O).
183 count_obj_list([X|Y],PathToObject,In,Out) :-
184 count_obj(X,el(PathToObject),In,I1),
185 count_obj(Y,PathToObject,I1,Out).
186
187 /* not_symmetric: check if object has symmetric permutations */
188 not_symmetric([]).
189 not_symmetric((X,Y)) :- not_symmetric(X), not_symmetric(Y).
190 not_symmetric(pred_false /* bool_false */).
191 not_symmetric(pred_true /* bool_true */).
192 not_symmetric(string(_)).
193 not_symmetric(fd(Nr,T)) :-
194 \+ b_global_deferred_set_with_card_gt1(T),
195 \+ is_unused_b_global_constant(T,Nr,_). /* TO DO: only makes sense if more than one unused global constant */
196 not_symmetric(int(_)).
197 /* add same for sets ?? */
198
199
200 % add_avl_count(usage_count(Nr,T,PathToObject),In,Out)
201 add_avl_count(AVL,Key,NewAVL) :- %print(add_count(Key)),nl,
202 (avl_fetch(Key,AVL,OldCount)
203 -> NewCount is OldCount+1,
204 avl_change(Key,AVL,_,NewAVL,NewCount)
205 ; avl_store(Key,AVL,1,NewAVL)
206 ). %, print(ok(NewCount)),nl.
207
208
209 /*
210 add_count(Var,Nr,T) :-
211 (retract(count(Nr,T,Var,C1))
212 -> (C is C1+1, assert(count(Nr,T,Var,C)))
213 ; assert(count(Nr,T,Var,1))
214 ). */
215
216 :- volatile deferred_set_el_marker/3.
217 :- dynamic deferred_set_el_marker/3.
218
219 /* compute individual markers for deferred set elements by hashing
220 classical path information using sha */
221
222 /*
223 compute_deferred_set_el_markers(CountAVL) :-
224 retractall(deferred_set_el_marker(_,_,_)),
225 avl_generate_el_markers(CountAVL,'$DUMMY',-1,[],OutType,OutNr,OutCount),
226 %% print(last_marker(OutType,OutNr,OutCount)),nl, %%
227 assert_def_marker(OutType,OutNr,OutCount).
228
229 avl_generate_el_markers(empty,CurType,CurElNr,CurCount,CurType,CurElNr,CurCount).
230 avl_generate_el_markers(node(usage_count(Nr,T,Path),Count,_,L,R),CurType,CurElNr,CurCount,
231 OutT,OutNr,OutCount) :-
232 avl_generate_el_markers(L,CurType,CurElNr,CurCount,T1,N1,C1),
233 ((N1=Nr,T1=T) % we are still treating the same deferred set element fd(T,Nr)
234 -> C2=[count(Path,Count)|C1] % add cur path to list of paths for fd(T,Nr)
235 ; assert_def_marker(T1,N1,C1), % assert cur paths for fd(T1,N1)
236 % We could use instead: avl_store(fd(T1,N1),AVL,el(T1,C1),NewAVL)
237 %print(marker(T1,N1,C1)),nl, %%
238 C2 = [count(Path,Count)] % start fresh; collecting paths for fd(T,Nr)
239 ), avl_generate_el_markers(R,T,Nr,C2,OutT,OutNr,OutCount).
240
241 :- use_module(extension('probhash/probhash')).
242 assert_def_marker(T1,N1,C1) :- raw_sha_hash(el(T1,C1),MHash),
243 assert(deferred_set_el_marker(T1,N1,MHash)).
244 */
245
246 /*
247 an attempt at not storing paths but just sort the fd-elements according to path
248 information and assign them a number: without adding id_couple below in marker/3 we get collisions, e.g, for
249 ./probcli examples/B/SymmetryReduction/PhilRing.mch -mc 1000 -p DEFAULT_SETSIZE 3 -p SYMMETRY_MODE hash -cc 13 47
250 */
251
252 compute_deferred_set_el_markers(CountAVL) :-
253 avl_to_list(CountAVL,AList),
254 %print(AList),nl,
255 merge_paths(AList,BList),
256 sort(BList,SBList),
257 %print(merged(SBList)),nl,
258 retractall(deferred_set_el_marker(_,_,_)),
259 assert_markers(SBList,0),!.
260
261 % merge all the information for same deferred set elements (Nr,Type)
262 merge_paths([],[]).
263 merge_paths([usage_count(Nr,Type,Path)-Count|Tail],Res) :-
264 merge_paths(Tail,Nr,Type,[count(Path,Count)],Res).
265
266 merge_paths([],Nr,Type,MarkerPaths,[marker(Type,MarkerPaths,Nr)]). % :- print(marker(Type,MarkerPaths,Nr)),nl.
267 merge_paths([usage_count(Nr,Type,Path)-Count|Tail],Nr,Type,M,Res) :- !,
268 merge_paths(Tail,Nr,Type,[count(Path,Count)|M],Res).
269 merge_paths([usage_count(Nr,Type,Path)-Count|Tail],Nr2,T2,M,[marker(T2,M,Nr2)|Res]) :-
270 % print(marker(T2,Nr2,M)),nl,
271 merge_paths(Tail,Nr,Type,[count(Path,Count)],Res).
272
273 :- use_module(extension('probhash/probhash')).
274 assert_markers([],_).
275 assert_markers([marker(Type,Paths,Nr)|T],ID) :-
276 get_same_markers(T,Type,Paths,Same,TT,NrSame), % get all other elements with same Paths information
277 ID1 is ID+1,
278 (NrSame=0 -> assert(deferred_set_el_marker(Type,Nr,fdunique(ID1,Type)))
279 ; assert_same_markers([Nr|Same],Type,ID1,NrSame)),
280 assert_markers(TT,ID1).
281
282 get_same_markers([marker(Type,Paths,Nr)|T],Type,Paths,[Nr|SM],TT,ResNr) :- !,
283 get_same_markers(T,Type,Paths,SM,TT,SNr), ResNr is SNr+1.
284 get_same_markers(T,_,_,[],T,0).
285
286
287 assert_same_markers([],_,_,_).
288 assert_same_markers([Nr|T],Type,ID,NrSame) :-
289 assert(deferred_set_el_marker(Type,Nr,fdsame(ID,Type,NrSame))),
290 assert_same_markers(T,Type,ID,NrSame).
291
292
293 /* */
294
295
296 % -------------------------------------------------------
297
298
299 /* marker(+ProBOBject, -TypeOfObject, -HashTerm) */
300
301
302 %:- use_module(b_global_sets,[all_elements_of_type/2]).
303 :- use_module(custom_explicit_sets).
304
305 /* missing: closure, records,... */
306 marker(closure(P,T,B),set(any),closure(P,T,B)).
307 marker(global_set(GS),set(Type),Local) :-
308 marker_explicit_set(global_set(GS),set(Type),Local).
309 marker(avl_set(S),set(Type),SLocal) :- % marker_explicit_set(avl_set(S),set(Type),Val,Local).
310 (avl_marker(S, Type,Local) -> true ; print(failed_avl_marker(S)),nl,fail),
311 sort_markers(Local,SLocal).
312 marker(closure_x(P,T,B,E),set(Type),Local) :-
313 marker_explicit_set(closure_x(P,T,B,E),set(Type),Local).
314 marker(int(N), integer, N). % int(N)
315 marker(string(S), string, S). % string(S)).
316 marker(fd(Nr,T), global(T), Term) :-
317 (deferred_set_el_marker(T,Nr,Term)
318 -> true
319 ; (b_global_deferred_set_with_card_gt1(T)
320 -> print(not_counted(Nr,T)),nl ; true),
321 Term=fd(Nr,T) /*should not happen for deferred sets */
322 ).
323 marker(pred_false /* bool_false */, boolean, pred_false).
324 marker(pred_true /* bool_true */, boolean, pred_true).
325 marker((X,Y), couple(T1,T2), Mark) :-
326 (X==Y -> Mark = id_couple(L1), marker(X,T1,L1) % added 7/9/2011 by leuschel
327 ; Mark = couple(L1,L2), marker(X,T1,L1),marker(Y,T2,L2)).
328 marker([], set(_), []).
329 marker([H|T], set(Type), Local) :-
330 l_marker([H|T],Type,LL),
331 sort_markers(LL,Local).
332
333
334 marker_explicit_set(ES,set(Type),Local) :-
335 expand_custom_set_to_list_now(ES,Res),
336 l_marker(Res,Type,LL),
337 sort_markers(LL,Local).
338
339
340 l_marker([], _Type, []).
341 l_marker([H|T], Type, [LH|LT]) :-
342 marker(H,Type,LH),
343 l_marker(T,Type,LT).
344
345 avl_marker(Avl, Type,Local) :- avl_domain(Avl,List),
346 l_marker(List,Type,Local).
347
348
349
350 /* example queries:
351 use_module(symmetry_marker),
352 symmetry_marker:compute_marker_for_state([bind(tr,[(fd(1,'Session'),fd(1,'Session')),(fd(1,'Session'),fd(2,'Session'))]),
353 bind(inv,[fd(1,'Session')]),bind(i,fd(1,'Session')),
354 bind(reach,[fd(1,'Session'),fd(2,'Session')]),bind(rac,[fd(1,'Session')])],M).
355
356 symmetry_marker:nodes_in_graph([(fd(1,'Session'),fd(1,'Session')),(fd(1,'Session'),fd(2,'Session'))],N).
357
358 use_module(symmetry_marker),symmetry_marker:cm.
359
360 use_module(symmetry_marker),
361 symmetry_marker:count_state([bind(tr,[(fd(1,'Session'),fd(1,'Session')),(fd(1,'Session'),fd(2,'Session'))]),
362 bind(inv,[fd(1,'Session')]),bind(i,fd(1,'Session')),
363 bind(reach,[fd(1,'Session'),fd(2,'Session')]),bind(rac,[fd(1,'Session')])]),
364 symmetry_marker:count(X,Y,Z).
365 */
366
367
368 :- use_module(library(samsort)).
369
370 sort_markers(X,SX) :- samsort(X,SX). % sort without duplicate removal
371
372
373
374 :- use_module(specfile,[expand_const_and_vars_to_full_store/2]).
375
376 :- use_module(self_check).
377 :- assert_must_succeed((symmetry_marker:compute_marker_for_state([bind(x,fd(1,'Name')),bind(y,fd(2,'Name'))],R1),
378 symmetry_marker:compute_marker_for_state([bind(x,fd(2,'Name')),bind(y,fd(1,'Name'))],R2),R1==R2)).
379
380 compute_marker_for_state(State,Marker) :-
381 (State = concrete_constants(BState)
382 -> Marker = concrete_constants(BMarker)
383 ; (State = csp_and_b(CSPState,BState)
384 -> Marker = csp_and_b(CSPState,BMarker)
385 ; (State=[] ; State = [_|_] ; State = const_and_vars(_,_) ),
386 State=BState, Marker=BMarker
387 )
388 ),
389 expand_const_and_vars_to_full_store(BState,FullBState),
390 count_state(FullBState), /* first count the number of occurrences
391 of every symmetric element in the State; this number will
392 be used as the marker for that element */
393 marker_for_state(FullBState,BMarker).
394 % ,raw_sha_hash(BMarker0,BMarker). % , print(marker_for_state(FullBState,BMarker)),nl,nl.
395
396 /* compute a marker for a given state */
397 marker_for_state([],[]).
398 marker_for_state([bind(Var,Val)|T],[M|TM]) :-
399 (non_symmetric_var(Var,_) -> TermMarker=Val
400 ; marker(Val, _Type,TermMarker)),
401 M=mark(Var,TermMarker),
402 marker_for_state(T,TM).
403
404
405 % TESTING CODE
406
407 /*
408 :- dynamic marker/2.
409 % symmetry_marker:cm.
410 % symmetry_marker:marker(10,M1), symmetry_marker:marker(18,M2), M1=M2.
411
412 :- public cm/0.
413 cm :-
414 print('Computing markers for state space'),nl,
415 retractall(marker(_,_)),
416 cms(ID, M),
417 assert(marker(ID,M)),
418 fail.
419 cm :-
420 marker(ID,Marker),
421 marker(ID2,Marker), ID2 > ID,
422 print(same_marker(ID,ID2)),nl,
423 fail.
424 cm :- print('Finished'),nl.
425
426 :- use_module(state_space,[visited_expression/2]).
427 cms(ID, M) :-
428 state_space:visited_expression(ID,State),
429 compute_marker_for_state(State,M),
430 print(marker(ID,State,M)),nl.
431
432
433 :- public test_sym/0.
434 test_sym :- state_space:visited_expression(ID,State),
435 print(ID), print(' : '),
436 sym_mark_for_state(State).
437
438 sym_mark_for_state(State) :-print(State),nl,
439 member(bind(Var,Val),State),
440 print(Var), print(' = '),
441 marker(Val, T,L),
442 print(marker(Val,T,L)),
443 print(' '),
444 nl,
445 fail.
446 sym_mark_for_state(_) :- nl.
447 */