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