| 1 | | % (c) 2009-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 | | :- module(b_global_sets, |
| 6 | | [b_get_global_constants/1, b_get_enumerated_set_constants/1, |
| 7 | | b_get_global_enumerated_sets/1, b_get_global_sets/1, |
| 8 | | lookup_global_constant/2, |
| 9 | | is_b_global_constant/3, |
| 10 | | is_b_global_constant_hash/3, % a variation with indexing of first two args |
| 11 | | is_used_b_global_constant/3, is_unused_b_global_constant/2, |
| 12 | | b_global_constant_or_set_identifier/1, |
| 13 | | exclude_global_identifiers/2, exclude_global_identifiers/3, |
| 14 | | |
| 15 | | b_global_set/1, b_non_empty_global_set/1, b_empty_global_set/1, |
| 16 | | b_global_deferred_set/1, |
| 17 | | b_global_set_with_potential_symmetry/1, b_global_deferred_set_with_card_gt1/1, |
| 18 | | b_partially_deferred_set/1, |
| 19 | | |
| 20 | | enumerated_set/1, |
| 21 | | unfixed_deferred_set/1, unfixed_deferred_set_exists/0, |
| 22 | | fixed_deferred_set_size/2, |
| 23 | | provably_finite_global_set/1, infinite_global_set/1, |
| 24 | | contains_unfixed_deferred_set/1, contains_unfixed_deferred_set/2, |
| 25 | | b_supplementary_global_set/1, % introduced when untyped ids are allowed |
| 26 | | |
| 27 | | inferred_minimum_global_set_cardinality/2, |
| 28 | | inferred_maximum_global_set_cardinality/2, |
| 29 | | b_exists_global_set_with_potential_symmetry/0, |
| 30 | | b_global_set_cardinality/2, |
| 31 | | |
| 32 | | b_clear_global_set_type_definitions/0, |
| 33 | | |
| 34 | | % three phases of precompilation: |
| 35 | | b_check_and_precompile_enumerated_sets/0, |
| 36 | | b_check_and_precompile_deferred_sets/0, |
| 37 | | b_check_and_precompile_global_set_symmetry/0, |
| 38 | | % can be called before precompilation: |
| 39 | | register_enumerated_sets/2, |
| 40 | | |
| 41 | | b_get_prob_deferred_set_elements/2, |
| 42 | | add_prob_deferred_set_elements_to_store/3, |
| 43 | | inline_prob_deferred_set_elements_into_bexpr/2, |
| 44 | | prob_deferred_set_element/4, |
| 45 | | |
| 46 | | find_inequal_global_set_identifiers/4, |
| 47 | | static_symmetry_reduction_for_global_sets/1, |
| 48 | | |
| 49 | | b_integer_set/1, b_integer_or_real_or_string_set/1, b_integer_or_real_set/1, |
| 50 | | b_type2_set/2, try_b_type2global_set/2, |
| 51 | | |
| 52 | | %b_fd_type/3, |
| 53 | | b_get_fd_type_bounds/3, |
| 54 | | b_fd_card/2, |
| 55 | | is_global_set_of_cardinality_one/2, %is_global_set_of_cardinality_two/3, |
| 56 | | global_type/2, global_type_wf/3, |
| 57 | | get_global_type_value/3, |
| 58 | | enum_global_type_limited/2, enumerate_global_type_with_enum_warning/4, |
| 59 | | |
| 60 | | all_elements_of_type/2, all_elements_of_type_wf/3, |
| 61 | | all_elements_of_type_rand_wf/3, |
| 62 | | |
| 63 | | set_user_defined_scope/2, get_user_defined_scope/4, |
| 64 | | |
| 65 | | generate_fresh_supplementary_global_set/1, |
| 66 | | register_replaced_global_set/2, b_replaced_global_set/2, |
| 67 | | %,add_global_set_with_named_constants/2 /* for use by Z,... */ |
| 68 | | list_contains_unfixed_deferred_set_id/1 |
| 69 | | ]). |
| 70 | | |
| 71 | | :- use_module(debug). |
| 72 | | :- use_module(tools). |
| 73 | | :- use_module(library(lists)). |
| 74 | | :- use_module(library(ordsets)). |
| 75 | | :- use_module(library(avl)). |
| 76 | | :- use_module(self_check). |
| 77 | | :- use_module(preferences). |
| 78 | | :- use_module(error_manager). |
| 79 | | :- use_module(bsyntaxtree). |
| 80 | | :- use_module(library(between),[between/3]). |
| 81 | | :- use_module(gensym,[gensym/2]). |
| 82 | | |
| 83 | | :- use_module(module_information,[module_info/2]). |
| 84 | | :- module_info(group,kernel). |
| 85 | | :- module_info(description,'This module provides support for deferred/enumerated set elements in B.'). |
| 86 | | |
| 87 | | :- use_module(bmachine,[b_get_properties_from_machine/1, |
| 88 | | b_get_machine_constants/1, b_get_machine_variables/1, |
| 89 | | b_get_named_machine_set/2, b_get_machine_set/1, |
| 90 | | b_get_all_used_identifiers/1, % required for symmetry-related infos |
| 91 | | b_get_disjoint_constants_of_type/3, |
| 92 | | b_extract_values_clause_assignment/3, |
| 93 | | b_get_typed_definition/3, b_get_machine_setscope/2]). |
| 94 | | |
| 95 | | /* what we call global sets here usually called "given sets", which can be |
| 96 | | either enumerated or deferred */ |
| 97 | | |
| 98 | | |
| 99 | | :- dynamic b_global_constant/3. % named element of a deferred or enumerated set |
| 100 | | % for enumerated sets S = {a,b,c} we would have entries a,b,c as b_global_constant |
| 101 | | % for deferred set S, we may add CONSTANTS which are element of S as b_global_constant |
| 102 | | :- dynamic used_b_global_constant/3. |
| 103 | | |
| 104 | | :- dynamic inferred_minimum_global_set_cardinality/2. |
| 105 | | :- dynamic inferred_maximum_global_set_cardinality/2. |
| 106 | | |
| 107 | | %% b_get_global_enumerated_sets(-GSets). |
| 108 | | % Returns all fully and partially enumerated sets. |
| 109 | | b_get_global_enumerated_sets(GSets) :- |
| 110 | | findall(GS, (b_global_set(GS), \+ b_global_deferred_set(GS)), GSets). |
| 111 | | b_get_global_sets(GSets) :- |
| 112 | | findall(GS, b_global_set(GS), GSets). |
| 113 | | |
| 114 | | b_get_global_constants(Csts) :- findall(Cst,is_b_global_constant(_,_,Cst),Csts). |
| 115 | | % only get those elements that have been explicitly marked as enumerated; excludes partially enumerated sets. |
| 116 | | %b_get_enumerated_set_constants(Set,Csts) :- enumerated_set(Set),findall(Cst,is_b_global_constant(Set,_,Cst),Csts). |
| 117 | | b_get_enumerated_set_constants(Csts) :- findall(Cst,(enumerated_set(Set),is_b_global_constant(Set,_,Cst)),Csts). |
| 118 | | |
| 119 | | % getting named elements of SETS: |
| 120 | | is_b_global_constant(Set,Nr,Cst) :- b_global_constant(Set,Nr,Cst). |
| 121 | | |
| 122 | | :- use_module(library(terms),[term_hash/2]). |
| 123 | | % efficient indexing if both GS and X are known, useful for large global sets |
| 124 | | is_b_global_constant_hash(GS,Nr,Name) :- |
| 125 | | term_hash(fd(Nr,GS),Hash), b_global_constant_hash(Hash,GS,Nr,Name). |
| 126 | | |
| 127 | | is_unused_b_global_constant(Set,Nr) :- |
| 128 | | b_get_fd_type_bounds_limited(Set,Low,Up), |
| 129 | ? | between(Low,Up,Nr), |
| 130 | | \+ used_b_global_constant(Set,Nr,_). |
| 131 | | is_used_b_global_constant(Set,Nr,Cst) :- |
| 132 | | used_b_global_constant(Set,Nr,Cst). |
| 133 | | |
| 134 | | two_unused_b_global_constants_exist(Set) :- |
| 135 | | b_get_fd_type_bounds_limited(Set,Low,Up), |
| 136 | | between(Low,Up,Nr), |
| 137 | | \+ used_b_global_constant(Set,Nr,_), |
| 138 | | !, |
| 139 | | Nr1 is Nr+1, |
| 140 | | between(Nr1,Up,Nr2), |
| 141 | | \+ used_b_global_constant(Set,Nr2,_). |
| 142 | | |
| 143 | | b_get_fd_type_bounds_limited(GlobalSet,Low,UpLimited) :- |
| 144 | | b_get_fd_type_bounds(GlobalSet,Low,Up), |
| 145 | | (Up=inf -> add_message(b_global_sets,'Limiting infinite deferred set elements to 999: ',GlobalSet), |
| 146 | | UpLimited=999 |
| 147 | | ; UpLimited=Up). |
| 148 | | |
| 149 | | :- dynamic lookup_global_constant/2, b_global_constant_hash/4. |
| 150 | | %lookup_global_constant(Id,Val) :- |
| 151 | | % b_global_constant(Set,Nr,Id), % not an indexed lookup ; now we assert lookup_global_constant below |
| 152 | | % Val = fd(Nr,Set). |
| 153 | | |
| 154 | | b_global_constant_or_set_identifier(Id) :- lookup_global_constant(Id,_). |
| 155 | ? | b_global_constant_or_set_identifier(Id) :- b_global_set(Id). |
| 156 | | % one could also think of freetypes |
| 157 | | |
| 158 | | % exclude global constants and sets from a list of ids |
| 159 | | exclude_global_identifiers([],[]). |
| 160 | | exclude_global_identifiers([TID|T],Res) :- |
| 161 | ? | (get_id(TID,ID),b_global_constant_or_set_identifier(ID) -> Res=RT ; Res = [TID|RT]), |
| 162 | | exclude_global_identifiers(T,RT). |
| 163 | | |
| 164 | | exclude_global_identifiers([],_,[]). |
| 165 | | exclude_global_identifiers([TID|T],Local,Res) :- |
| 166 | | (get_id(TID,ID), b_global_constant_or_set_identifier(ID), |
| 167 | | ord_nonmember(ID,Local) |
| 168 | | -> Res=RT ; Res = [TID|RT]), |
| 169 | | exclude_global_identifiers(T,Local,RT). |
| 170 | | |
| 171 | | % works both with typed and untyped ids |
| 172 | | get_id(b(identifier(ID),_,_), R) :- !, R=ID. |
| 173 | | get_id(ID,ID) :- (atom(ID) -> true ; add_warning(b_global_sets,'Not an ID: ',ID)). |
| 174 | | |
| 175 | | :- dynamic b_precompiled_global_set/1. |
| 176 | | b_precompiled_global_set(_) :- print_error('*** b_global_set not precompiled'),fail. |
| 177 | | |
| 178 | ? | b_global_set(GS) :- b_precompiled_global_set(GS) ; b_supplementary_global_set(GS). |
| 179 | | |
| 180 | | % a version of b_global_set which does not leave a trailing choice point for b_supplementary_global_set around |
| 181 | | % has to be called with GS ground |
| 182 | | %b_global_set_ground(GS) :- if(b_precompiled_global_set(GS),true,b_supplementary_global_set(GS)). |
| 183 | | |
| 184 | | % Does not contain partially enumerated sets. |
| 185 | | b_global_deferred_set(GS) :- |
| 186 | ? | b_global_set(GS), |
| 187 | | \+ is_b_global_constant(GS,_Nr,_Cst). % note : some deferred sets are translated into partially_deferred_set |
| 188 | | |
| 189 | | % either an enumerated set with unused constants or a deferred set where some constants were lifted into the deferred set |
| 190 | | b_partially_deferred_set(GS) :- |
| 191 | | b_global_set(GS), |
| 192 | | (is_b_global_constant(GS,_Nr,_Cst) -> true), % GS is in principle enumerated |
| 193 | | (is_unused_b_global_constant(GS,_X) -> true). |
| 194 | | |
| 195 | | |
| 196 | | b_global_set_with_potential_symmetry(GS) :- |
| 197 | ? | b_global_set(GS), |
| 198 | ? | (b_global_deferred_set_with_card_gt1(GS) -> true |
| 199 | | ; two_unused_b_global_constants_exist(GS) -> true % At least two unused constants exist |
| 200 | | ; fail). |
| 201 | | |
| 202 | | b_global_deferred_set_with_card_gt1(GS) :- |
| 203 | ? | b_global_deferred_set(GS), |
| 204 | | extract_setsize_from_machine_cache(GS,Low,Up), |
| 205 | | inf_greater(Up,Low). |
| 206 | | |
| 207 | | inf_greater(X,Y) :- (X=inf -> integer(Y) ; X>Y). |
| 208 | | inf_add(X,Y,XY) :- (X=inf -> XY=inf ; Y=inf -> XY=inf ; XY is X+Y). |
| 209 | | |
| 210 | | :- volatile b_exists_global_set_with_potential_symmetry/0. |
| 211 | | :- dynamic b_exists_global_set_with_potential_symmetry/0. |
| 212 | | :- dynamic precompilation_phase/1. |
| 213 | | |
| 214 | | % ------------------------------------------- |
| 215 | | |
| 216 | | % Phase 1 of precompilation: |
| 217 | | b_check_and_precompile_enumerated_sets :- |
| 218 | | b_reset_global_set_type_definitions, |
| 219 | | retractall(b_exists_global_set_with_potential_symmetry), |
| 220 | | retractall(precompilation_phase(_)), |
| 221 | | assertz(precompilation_phase(1)), |
| 222 | | debug_println(9,'Preprocessing enumerated sets: '), |
| 223 | | debug_print(9,'% '), |
| 224 | | b_get_named_machine_set(Set,_Els), % treat enumerated sets first; their cardinality is obvious |
| 225 | | precompile_global_set(Set), % will only require b_get_named_machine_set/2 from bmachine |
| 226 | | fail. |
| 227 | | b_check_and_precompile_enumerated_sets :- |
| 228 | | retractall(precompilation_phase(_)), |
| 229 | | assertz(precompilation_phase(2)), |
| 230 | | debug_nl(9). |
| 231 | | |
| 232 | | % Phase 2 of precompilation: |
| 233 | | b_check_and_precompile_deferred_sets :- |
| 234 | | (deferred_sets_precompiled |
| 235 | | -> add_internal_error('Deferred sets already precompiled',b_check_and_precompile_deferred_sets) |
| 236 | | ; enumerated_sets_precompiled -> true |
| 237 | | ; add_internal_error('Enumerated sets not yet precompiled',b_check_and_precompile_deferred_sets)), |
| 238 | | retractall(precompilation_phase(_)), |
| 239 | | assertz(precompilation_phase(3)), |
| 240 | | check_enumerated_set_scope(_), |
| 241 | | fail. |
| 242 | | b_check_and_precompile_deferred_sets :- |
| 243 | | debug_println(9,'Inferring cardinality and disjoint elements of deferred sets: '), |
| 244 | | debug_print(9,'% '), |
| 245 | ? | b_get_machine_set(Set), |
| 246 | | \+ b_get_named_machine_set(Set,_), % not enumerated |
| 247 | | precompile_global_set(Set), |
| 248 | | fail. |
| 249 | | b_check_and_precompile_deferred_sets :- |
| 250 | | retractall(precompilation_phase(_)), |
| 251 | | assertz(precompilation_phase(4)), |
| 252 | | debug_nl(9). |
| 253 | | |
| 254 | | enumerated_sets_precompiled :- precompilation_phase(X), X >= 2. |
| 255 | | deferred_sets_precompiled :- precompilation_phase(X), X >= 4. |
| 256 | | |
| 257 | | precompile_global_set(Set) :- |
| 258 | | add_new_global_set(Set), |
| 259 | ? | (b_extract_fd_type(Set,LowBnd,UpBnd) /* also computes & caches the size of the SET */ |
| 260 | | -> debug_print(9,' '),debug_print(9,Set), |
| 261 | | debug_print(9,'=='),debug_print(9,LowBnd),debug_print(9,'..'),debug_print(9,UpBnd) |
| 262 | | ; add_internal_error('No b_extract_fd_type/3 solution for global set: ',b_extract_fd_type(Set,_,_)) |
| 263 | | ),debug_print(9,' '). |
| 264 | | |
| 265 | | % check if there are any conflicts between scope annotations and enumerated set |
| 266 | | check_enumerated_set_scope(GlobalSetName) :- |
| 267 | | enumerated_set(GlobalSetName), |
| 268 | | extract_setsize_from_machine_cache(GlobalSetName,LowBound,UpBound), |
| 269 | | (get_user_defined_scope(GlobalSetName,DL,DU,Span), |
| 270 | | DU-DL =\= UpBound-LowBound |
| 271 | | -> UserSize is 1+DU-DL, |
| 272 | | EnumSize is 1+UpBound-LowBound, |
| 273 | | ajoin(['Conflict between cardinality of scope_',GlobalSetName, |
| 274 | | ' DEFINITION (',UserSize,') and enumeration (',EnumSize,') of SET:'],Msg), |
| 275 | | add_error(extract_setsize_from_machine,Msg,GlobalSetName,Span) |
| 276 | | ; true |
| 277 | | ). |
| 278 | | |
| 279 | | :- use_module(library(avl),[avl_fetch/2]). |
| 280 | | % Phase 3 of precompilation: |
| 281 | | % This can be done after bmachine pre-compile is finished |
| 282 | | b_check_and_precompile_global_set_symmetry :- |
| 283 | | (retract(precompilation_phase(4)) -> true |
| 284 | | ; add_internal_error('Deferred sets not yet precompiled',b_check_and_precompile_global_set_symmetry)), |
| 285 | | assertz(precompilation_phase(5)), |
| 286 | | find_all_used_enumerated_elements(UsedEnumsAVL), |
| 287 | ? | b_extract_fd_type(GS,_Low,_Up), |
| 288 | | (is_b_global_constant(GS,_Nr,_Cst) -> true), % GS is in principle enumerated |
| 289 | | (user_forced_symmetry(GS) |
| 290 | | -> print('FORCING SYMMETRY: '), print(GS), nl, |
| 291 | | fail % not asserting used_b_global_constant; every constant becomes "virtually" unused |
| 292 | | ; true |
| 293 | | ), |
| 294 | | is_b_global_constant_hash(GS,X,Name), |
| 295 | | avl_fetch(Name,UsedEnumsAVL), |
| 296 | | assertz(used_b_global_constant(GS,X,deferred)), % by storing that it is not used, we enable symmetry reduction |
| 297 | | debug_println(9,used_b_global_constant(GS:Name)), |
| 298 | | fail. |
| 299 | | b_check_and_precompile_global_set_symmetry :- %print(precomp_gs_3), debug:nl_time, |
| 300 | ? | (b_global_set_with_potential_symmetry(_) |
| 301 | | -> assertz(b_exists_global_set_with_potential_symmetry), |
| 302 | | debug_println(9,'% Symmetry is potentially useful for this machine') |
| 303 | | ; true), |
| 304 | | debug_nl(9), |
| 305 | | /* clean up: */ |
| 306 | | reset_global_set_user_defined_scope. |
| 307 | | |
| 308 | | % ------------------------------------------- |
| 309 | | |
| 310 | | % peform MACE style static symmetry reduction for those global constants |
| 311 | | % that have not already been fixed |
| 312 | | % e.g., for constants aa,bb,cc,dd of type ID and bb/=cc --> nrs of bb,cc will be fixed as 1 and 2; we will ensure that numbers of aa:1..3 and dd:1..4 and that dd=4 only if aa=3 |
| 313 | | |
| 314 | | static_symmetry_reduction_for_global_sets(_ConstantsState) :- |
| 315 | | get_preference(use_solver_on_load,kodkod),!. % no idea which numbering Kodkod might return/expect ?! |
| 316 | | static_symmetry_reduction_for_global_sets(_ConstantsState) :- |
| 317 | | get_preference(use_static_symmetry_detection,false),!. |
| 318 | | static_symmetry_reduction_for_global_sets(ConstantsState) :- |
| 319 | | findall(gs_info(GS,FirstAvailableNewIdx,Low,Up,Other), |
| 320 | | static_symmetry_reduction_possible(GS,FirstAvailableNewIdx,Low,Up,Other),L), |
| 321 | | maplist(perform_static_symmetry_reduction(ConstantsState),L). |
| 322 | | |
| 323 | | :- use_module(static_symmetry_reduction,[static_symmetry_reduction_possible/5, perform_ssr/8]). |
| 324 | | perform_static_symmetry_reduction(ConstantsState,gs_info(GS,First,Low,Up,Other)) :- |
| 325 | | if(perform_ssr(Other,[],First,First,GS,Low,Up,ConstantsState),true, |
| 326 | | add_internal_error('Call failed: ',perform_ssr(Other,[],First,First,GS,Low,Up,ConstantsState))). |
| 327 | | |
| 328 | | |
| 329 | | |
| 330 | | % -------------------- |
| 331 | | |
| 332 | | |
| 333 | | :- use_module(avl_tools,[ord_domain_list_to_avl/2]). |
| 334 | | find_all_used_enumerated_elements(ElementsAVL) :- |
| 335 | | b_get_all_used_identifiers(Identifiers), |
| 336 | | b_get_machine_variables(TVariables),get_texpr_ids(TVariables,Variables), |
| 337 | | % We assume that b_get_all_used_identifiers/1 is a proper ordset. (sorted with sort/2) |
| 338 | | % As an optimisation, we remove the variables, they cannot be enumerated elements. |
| 339 | | list_to_ord_set(Variables,OVariables), |
| 340 | | ord_subtract(Identifiers,OVariables,Elements), |
| 341 | | ord_domain_list_to_avl(Elements,ElementsAVL),!. |
| 342 | | find_all_used_enumerated_elements(_Elements) :- |
| 343 | | add_error_and_fail(b_global_sets, 'find_all_used_enumerated_elements failed'). |
| 344 | | |
| 345 | | |
| 346 | | reset_global_set_user_defined_scope :- |
| 347 | | retract(global_set_user_defined_scope(GS,_Scope)), |
| 348 | | (is_b_precompiled_globalset(GS) |
| 349 | | -> true |
| 350 | | ; add_error(b_global_sets,'Trying to set scope of unknown SET: ',GS) |
| 351 | | ),fail. |
| 352 | | reset_global_set_user_defined_scope. |
| 353 | | |
| 354 | | % Return those constants which are implicitly introduced by ProB, but not really |
| 355 | | % part of the model. These are the elements of a deferred set. |
| 356 | | b_get_prob_deferred_set_elements(TIds,AllOrVisible) :- |
| 357 | | findall( TId, |
| 358 | | ( prob_deferred_set_element(GS,_Nr,Id,AllOrVisible), |
| 359 | | create_texpr(identifier(Id),global(GS),[],TId)), |
| 360 | | TIds). |
| 361 | | |
| 362 | | |
| 363 | | |
| 364 | | :- use_module(bsyntaxtree,[transform_bexpr_with_scoping/3]). |
| 365 | | % inline probids (prob deferred set ids) as values into a predicate or expression |
| 366 | | inline_prob_deferred_set_elements_into_bexpr(Pred,CompiledPred) :- |
| 367 | | add_prob_deferred_set_elements_to_store([], State, visible), % visible: only those Ids which do not clash with variables, constants, ... are added |
| 368 | | State \= [], !, |
| 369 | | sort(State,S), |
| 370 | | debug_println(19,compiling_probids_into_bexpr(S)), |
| 371 | | transform_bexpr_with_scoping(b_global_sets:inline_state(S),Pred,CompiledPred). |
| 372 | | inline_prob_deferred_set_elements_into_bexpr(Pred,Pred). |
| 373 | | |
| 374 | | :- use_module(tools_lists,[ord_member_nonvar_chk/2]). |
| 375 | | :- use_module(library(ordsets),[ord_nonmember/2]). |
| 376 | | % difference with compile from b_compiler: no need to specify parameters, no errors if identifier not found |
| 377 | | inline_state(State,b(identifier(ID),Type,I),b(value(Val),Type,I),LocalIds) :- |
| 378 | | ord_member_nonvar_chk(bind(ID,Val),State), % the ID appears in the binding list |
| 379 | | ord_nonmember(ID,LocalIds). % The ID is not overriden by a locally quantified variable |
| 380 | | |
| 381 | | add_prob_deferred_set_elements_to_store(OldStore,NewStore,AllOrVisible) :- % add prob_ids |
| 382 | | is_list(OldStore),!, |
| 383 | | %print(add_prob_deferred_set_elements_to_store(OldStore)),nl, |
| 384 | | findall(bind(Id,fd(Nr,GS)),(prob_deferred_set_element(GS,Nr,Id,AllOrVisible), |
| 385 | | \+ member(bind(Id,_),OldStore)), |
| 386 | | NewStore, OldStore). |
| 387 | | add_prob_deferred_set_elements_to_store(OldStore,NewStore,_) :- |
| 388 | | add_internal_error('Cannot add deferred set elements to this store: ',OldStore), |
| 389 | | NewStore=OldStore. |
| 390 | | |
| 391 | | % virtual deferred set elements generated by ProB |
| 392 | | prob_deferred_set_element(GlobalSet,Elem,Id,AllOrVisible) :- |
| 393 | ? | b_global_set(GlobalSet), |
| 394 | | atom_codes(GlobalSet,GlobalSetCodes),append(GlobalSetCodes,ECodes,NameCodes), |
| 395 | | b_get_fd_type_bounds(GlobalSet,Low,Up), |
| 396 | | (Up=inf -> UpLimited=999, |
| 397 | | debug_format(19,'Limiting ProB IDs for deferred set ~w to ~w..~w elements~n',[GlobalSet,Low,UpLimited]) |
| 398 | | ; Up>999 -> UpLimited=999, % TODO: provide preference/option ? |
| 399 | | debug_format(19,'Limiting ProB IDs for deferred set ~w to ~w..~w elements~n',[GlobalSet,Low,UpLimited]) |
| 400 | | ; UpLimited=Up), |
| 401 | ? | between(Low,UpLimited,Elem), |
| 402 | | (AllOrVisible = all -> true |
| 403 | | ; % only visible ones |
| 404 | | \+ is_b_global_constant_hash(GlobalSet,Elem,_) % the identifier is not used somewhere else |
| 405 | | ), |
| 406 | | number_codes(Elem,ECodes), |
| 407 | | atom_codes(Id,NameCodes), |
| 408 | | \+ b_global_set(Id), % not used as another SET name |
| 409 | | \+ lookup_global_constant(Id,_). % not used as another SET element |
| 410 | | |
| 411 | | |
| 412 | | user_forced_symmetry(GS) :- % check if the user defined FORCE_SYMMETRY_GS == TRUE |
| 413 | | string_concatenate('FORCE_SYMMETRY_',GS,DefName), |
| 414 | | b_get_typed_definition(DefName,[],F), get_texpr_expr(F,boolean_true). |
| 415 | | |
| 416 | | |
| 417 | | %is_global_set_of_cardinality_two(Type,LowBnd,UpBnd) :- |
| 418 | | % b_get_fd_type_bounds(Type,LowBnd,UpBnd), |
| 419 | | % LowBnd+1 =:= UpBnd. |
| 420 | | |
| 421 | | :- volatile is_global_set_of_cardinality_one/2. |
| 422 | | :- dynamic is_global_set_of_cardinality_one/2. |
| 423 | | % should be called when unloading a machine, before type-checking etc... |
| 424 | | % TO DO: use :- use_module(eventhandling,[register_event_listener/3]). |
| 425 | | b_clear_global_set_type_definitions :- % nl,print(clearing_gs),nl, |
| 426 | | retractall(b_supplementary_global_set(_)), |
| 427 | | retractall(b_replaced_global_set(_,_)), |
| 428 | | b_reset_global_set_type_definitions, |
| 429 | | retractall(global_set_user_defined_scope(_,_)). |
| 430 | | |
| 431 | | b_reset_global_set_type_definitions :- %nl,print(reset_gs),nl, |
| 432 | | % print_message('resetting global sets'), |
| 433 | | retractall(b_precompiled_global_set(_)), |
| 434 | | retractall(enumerated_set(_)), |
| 435 | | retractall(fixed_deferred_set_size(_,_)), |
| 436 | | retractall(extract_setsize_from_machine_cache(_,_,_)), |
| 437 | | retractall(find_minimum_cardinality_cache(_,_)), |
| 438 | | retractall(is_global_set_of_cardinality_one(_,_)), |
| 439 | | retractall(b_global_constant(_,_,_)), |
| 440 | | retractall(b_global_constant_hash(_,_,_,_)), |
| 441 | | retractall(lookup_global_constant(_,_)), |
| 442 | | retractall(used_b_global_constant(_,_,_)), |
| 443 | | retractall(inferred_minimum_global_set_cardinality(_,_)), |
| 444 | | retractall(inferred_maximum_global_set_cardinality(_,_)). |
| 445 | | |
| 446 | | |
| 447 | | add_new_global_set(Set) :- |
| 448 | | (b_precompiled_global_set(Set) |
| 449 | | -> add_internal_error('Global set already exists: ',add_new_global_set(Set)) |
| 450 | | ; b_integer_or_real_or_string_set(Set) |
| 451 | | % should only occur in Event-B mode, or possibly in Z, TLA; in Atelier-B these are reserved keywords |
| 452 | | % this can cause problems as we currently create symbolic set values global_set('STRING'), ... |
| 453 | | -> add_warning(add_new_global_set,'Global set name can cause internal name clashes: ',Set), |
| 454 | | assertz(b_precompiled_global_set(Set)) |
| 455 | | ; assertz(b_precompiled_global_set(Set))). |
| 456 | | |
| 457 | | |
| 458 | | :- dynamic b_supplementary_global_set/1, b_replaced_global_set/2. |
| 459 | | b_supplementary_global_set(GS) :- b_supplementary_global_set(GS). |
| 460 | | |
| 461 | | % these should be called before we precompile the global set definitions |
| 462 | | generate_fresh_supplementary_global_set(FRESHID) :- |
| 463 | | gensym('__DEFERREDSET__',FRESHID), |
| 464 | | debug_println(10,generate_fresh_supplementary_global_set(FRESHID)), |
| 465 | | assertz(b_supplementary_global_set(FRESHID)). |
| 466 | | |
| 467 | | % called by record construction, when a global deferred set gets replaced by something else |
| 468 | | % we register it here for safety and better error messages |
| 469 | | % in some contexts (like freetype constructors/destructors) the old type may accidentally get used; but we should remove all references to old type |
| 470 | | register_replaced_global_set(ID,_) :- |
| 471 | | b_global_set(ID),!, |
| 472 | | add_warning(b_global_sets,'Global set already exists: ',ID). |
| 473 | | register_replaced_global_set(ID,_) :- |
| 474 | | b_replaced_global_set(ID,_),!, |
| 475 | | add_warning(b_global_sets,'Global set already registered as replaced: ',ID). |
| 476 | | register_replaced_global_set(ID,NewTypeExpr) :- |
| 477 | | assertz(b_replaced_global_set(ID,NewTypeExpr)). |
| 478 | | |
| 479 | | /* --------------------------------------------------------- */ |
| 480 | | /* Extracting Finite Domain type information from the B machine */ |
| 481 | | /* --------------------------------------------------------- */ |
| 482 | | |
| 483 | | |
| 484 | | |
| 485 | | /* below treats annotations in the form: |
| 486 | | DEFINITIONS |
| 487 | | scope_Name == 1..3; |
| 488 | | scope_Code == 4..8 |
| 489 | | which inform us about which finidte domain ranges we should |
| 490 | | give to global sets defined in SETS |
| 491 | | */ |
| 492 | | |
| 493 | | :- use_module(tools_printing,[print_error/1, format_error_with_nl/2]). |
| 494 | | :- dynamic extract_setsize_from_machine_cache/3. |
| 495 | | extract_setsize_from_machine_cache(_,_,_) :- |
| 496 | | print_error('*** extract_setsize_from_machine not precompiled'),fail. |
| 497 | | |
| 498 | | try_extract_setsize_from_machine(GlobalSetName,_LowBound,_UpBound) :- |
| 499 | | start_extracting_setsize(GlobalSetName),!,fail. % cyclic dependency; abort computation |
| 500 | | try_extract_setsize_from_machine(GlobalSetName,LowBound,UpBound) :- |
| 501 | | extract_setsize_from_machine(GlobalSetName,LowBound,UpBound). |
| 502 | | |
| 503 | | % Determine if GlobalSetName is enumerated or deferred and if we can infer cardinality (bounds) |
| 504 | | extract_setsize_from_machine(GlobalSetName,LowBound,UpBound) :- |
| 505 | | extract_setsize_from_machine_cache(GlobalSetName,L,U),!, |
| 506 | | LowBound=L,UpBound=U. |
| 507 | | extract_setsize_from_machine(GlobalSetName,LowBound,UpBound) :- |
| 508 | | start_extracting_setsize_from_machine(GlobalSetName), |
| 509 | | b_get_named_machine_set(GlobalSetName,ListOfConstants), % find explicitly declared ENUMERATED SETS |
| 510 | | !, |
| 511 | | assert_enumerated_set(GlobalSetName,ListOfConstants,LowBound,UpBound). % will also assert_extract_setsize_from_machine_cache |
| 512 | | extract_setsize_from_machine(GlobalSetName,L,U) :- |
| 513 | | b_supplementary_global_set(GlobalSetName),!, % deferred set that was added for untyped ID,... |
| 514 | | default_deferred_set_bounds(LowBound,UpBound), |
| 515 | | assert_extract_setsize_from_machine_cache(GlobalSetName,LowBound,UpBound), |
| 516 | | (LowBound,UpBound)=(L,U). |
| 517 | | extract_setsize_from_machine(GlobalSetName,L,U) :- |
| 518 | | /* we have a DEFERRED SET */ |
| 519 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,LowBound,UpBound), |
| 520 | | (LowBound=UpBound -> assertz(is_global_set_of_cardinality_one(GlobalSetName,LowBound)) ; true), |
| 521 | | assert_extract_setsize_from_machine_cache(GlobalSetName,LowBound,UpBound), |
| 522 | | % determine if some of the elements are ENUMERATED: |
| 523 | | (b_get_disjoint_constants_of_type(GlobalSetName, DisjointConstants,_) |
| 524 | | % DISABLE for Disprover ?? what if selected hyps does not contain this |
| 525 | | -> /* add those constants to the deferred set as though they were enumerated set elements: improves performance by avoiding redundant permutations; is a kind of symmetry breaking */ |
| 526 | | debug_format(19,'Adding enumerated disjoint constants ~w : ~w~n~n',[GlobalSetName,DisjointConstants]), |
| 527 | | add_named_constants_to_global_set(GlobalSetName,LowBound,DisjointConstants) |
| 528 | | ; true |
| 529 | | ), |
| 530 | | debug_println(9,setsize(GlobalSetName,LowBound,UpBound)), |
| 531 | | (LowBound,UpBound)=(L,U). |
| 532 | | |
| 533 | | |
| 534 | | /* for Deferred SETS : */ |
| 535 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,L,U) :- |
| 536 | | b_extract_values_clause_assignment(GlobalSetName,_Type,TVal), % VALUES clause |
| 537 | | (get_interval(TVal,Low,Up) -> true |
| 538 | | ; add_error(b_global_sets,'VALUES clause must set deferred set to an interval: ',GlobalSetName,TVal),fail), |
| 539 | | (evaluable_integer_expression(Low,[],LowBound) -> true |
| 540 | | ; add_error(b_global_sets,'Cannot extract lower bound from VALUES interval for: ',GlobalSetName,Low),fail), |
| 541 | | (evaluable_integer_expression(Up,[],UpBound) -> true |
| 542 | | ; add_error(b_global_sets,'Cannot extract upper bound from VALUES interval for: ',GlobalSetName,Up),fail), |
| 543 | | !, |
| 544 | | check_compatible_with_user_scope(GlobalSetName,UpBound,LowBound), |
| 545 | | LowBound=L,UpBound=U. |
| 546 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,L,U) :- |
| 547 | | b_get_properties_from_machine(Properties), member_in_conjunction(C,Properties), |
| 548 | | (is_equality_card_global_set(C,GlobalSetName,Properties,Card) |
| 549 | | % check if the PROPERTIES contain an expression of the form card(GS) = Nr |
| 550 | | -> true |
| 551 | | ; is_infinite_global_set_pred(C,GlobalSetName) |
| 552 | | % look for predicates which stipulate that the set is infinite |
| 553 | | -> Card=inf |
| 554 | | ), |
| 555 | | !, |
| 556 | | assertz(fixed_deferred_set_size(GlobalSetName,Card)), |
| 557 | | LowBound=1, UpBound = Card, |
| 558 | | check_compatible_with_user_scope(GlobalSetName,UpBound,LowBound), |
| 559 | | LowBound=L,UpBound=U. |
| 560 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,L,U) :- /* check if there is a scope_ DEFINITION */ |
| 561 | | get_user_defined_scope(GlobalSetName,LowBound,UpBound,_),!, |
| 562 | | LowBound=L,UpBound=U. |
| 563 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,LowBound,UpBound) :- |
| 564 | | get_preference(globalsets_fdrange,DefaultUpperBound), |
| 565 | | find_minimum_cardinality(GlobalSetName,MinCard), |
| 566 | | assertz(inferred_minimum_global_set_cardinality(GlobalSetName,MinCard)), |
| 567 | | !, |
| 568 | | LowBound = 1, |
| 569 | | (find_maximum_cardinality(GlobalSetName,MaxCard) |
| 570 | | -> (MaxCard=MinCard -> assertz(fixed_deferred_set_size(GlobalSetName,MinCard)), |
| 571 | | debug_println(9,fixed_deferred_set_size(GlobalSetName,MinCard)), |
| 572 | | UpBound = MinCard |
| 573 | | ; DefaultUpperBound>MaxCard -> |
| 574 | | debug_println(9,reducing_deferred_set_size(GlobalSetName,MaxCard)), |
| 575 | | UpBound=MaxCard |
| 576 | | ; DefaultUpperBound<MinCard -> |
| 577 | | debug_println(9,inferred_minimum_global_set_cardinality(GlobalSetName,MinCard)), |
| 578 | | UpBound=MinCard |
| 579 | | ; UpBound is MinCard |
| 580 | | ) |
| 581 | | ; MinCard>DefaultUpperBound -> UpBound=MinCard |
| 582 | | ; UpBound=DefaultUpperBound), |
| 583 | | debug_println(4,inferred(GlobalSetName,UpBound)). |
| 584 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,LowBound,UpBound) :- |
| 585 | | % No Minimum cardinality was inferred |
| 586 | | LowBound=1, get_preference(globalsets_fdrange,DefaultUpperBound), |
| 587 | | find_maximum_cardinality(GlobalSetName,MaxCard), |
| 588 | | !, |
| 589 | | (MaxCard=1 -> UpBound=1, assertz(fixed_deferred_set_size(GlobalSetName,1)) |
| 590 | | ; inf_greater(DefaultUpperBound,MaxCard) -> |
| 591 | | debug_println(9,reducing_deferred_set_size(GlobalSetName,MaxCard)), |
| 592 | | UpBound=MaxCard |
| 593 | | % does not have the case: DefaultUpperBound<MinCard -> UpBound=MinCard |
| 594 | | ; UpBound=DefaultUpperBound). |
| 595 | | extract_DEFERRED_setsize_from_machine(_GlobalSetName,LowBound,UpBound) :- |
| 596 | | % No Minimum cardinality was inferred |
| 597 | | default_deferred_set_bounds(LowBound,UpBound). |
| 598 | | |
| 599 | | default_deferred_set_bounds(1,UpBound) :- get_preference(globalsets_fdrange,UpBound). |
| 600 | | |
| 601 | | check_compatible_with_user_scope(GlobalSetName,UpBound,LowBound) :- |
| 602 | | (get_user_defined_scope(GlobalSetName,DL,DU,Span), |
| 603 | | DU-DL =\= UpBound-LowBound |
| 604 | | -> add_error(extract_DEFERRED_setsize_from_machine,'Conflict between scope_ Definition and PROPERTIES:',GlobalSetName,Span) |
| 605 | | ; true |
| 606 | | ). |
| 607 | | |
| 608 | | |
| 609 | | find_maximum_cardinality(GlobalSetName,MaxCard) :- |
| 610 | | find_maximum_cardinality1(GlobalSetName,MaxCard), |
| 611 | | assertz(inferred_maximum_global_set_cardinality(GlobalSetName,MaxCard)). |
| 612 | | |
| 613 | | find_maximum_cardinality1(GlobalSetName,MaxCard) :- |
| 614 | | b_get_machine_constants(Csts), |
| 615 | | b_get_properties_from_machine(Properties), |
| 616 | | findall(MC2,find_maximum_cardinality2(Csts,Properties,GlobalSetName,MC2),List), |
| 617 | | min_member(MaxCard,List). |
| 618 | | find_maximum_cardinality2(_Csts,Properties,GlobalSetName,MaxCard) :- |
| 619 | | get_card_upper_bound(Properties,GlobalSetName,MaxCard). |
| 620 | | find_maximum_cardinality2(_Csts,Properties,GlobalSetName,MaxCard) :- |
| 621 | | get_equality(Properties,GlobalSetName,EXPR,RestProp), % GlobalSetName = EXPR |
| 622 | | maximum_cardinality_of_expression(EXPR,RestProp,MaxCard). |
| 623 | | find_maximum_cardinality2(_Csts,Properties,GlobalSetName,MaxCard) :- |
| 624 | | member_in_conjunction(C,Properties), |
| 625 | | get_texpr_expr(C,partition(Set,DisjSets)), % partition(GlobalSetName, DisjSets) |
| 626 | | global_set_identifier(Set,GlobalSetName), |
| 627 | | max_card_of_partition_list(DisjSets,Properties,0,MaxCard). |
| 628 | | |
| 629 | | |
| 630 | | get_equality(Properties,ID,R,RestProp) :- |
| 631 | | select_member_in_conjunction(C,Properties,RestProp), |
| 632 | | get_texpr_expr(C,equal(LHS,RHS)), |
| 633 | | ( get_texpr_expr(LHS,identifier(ID)), R=RHS |
| 634 | | ; get_texpr_expr(RHS,identifier(ID)), R=LHS ). |
| 635 | | |
| 636 | | % find card(ID) <= MaxCard |
| 637 | | get_card_upper_bound(Properties,ID,MaxCard) :- |
| 638 | | LHS = b(card(TID),_,_), |
| 639 | | get_texpr_expr(TID,identifier(ID)), |
| 640 | | select_member_in_conjunction(C,Properties,RestProp), |
| 641 | | ( get_texpr_expr(C,less_equal(LHS,RHS)), Strict=false ; |
| 642 | | get_texpr_expr(C,greater_equal(RHS,LHS)), Strict=false ; |
| 643 | | get_texpr_expr(C,less(LHS,RHS)), Strict=true ; |
| 644 | | get_texpr_expr(C,greater(RHS,LHS)), Strict=true |
| 645 | | ), |
| 646 | | evaluable_integer_expression(RHS,RestProp,R), |
| 647 | | (Strict=true -> MaxCard is R-1 ; MaxCard = R). |
| 648 | | |
| 649 | | maximum_cardinality_of_expression(b(E,_,_),Props,MC) :- maximum_cardinality_of_expression2(E,Props,MC). |
| 650 | | maximum_cardinality_of_expression2(set_extension(SetExtElements),_,MaxCard) :- |
| 651 | | length(SetExtElements,MaxCard). |
| 652 | | % we assume that all elements in List can be different |
| 653 | | maximum_cardinality_of_expression2(union(A,B),Props,MaxCard) :- |
| 654 | | maximum_cardinality_of_expression(A,Props,MA), |
| 655 | | maximum_cardinality_of_expression(B,Props,MB), MaxCard is MA+MB. |
| 656 | | maximum_cardinality_of_expression2(intersection(A,B),Props,MaxCard) :- |
| 657 | | maximum_cardinality_of_expression(A,Props,MA), |
| 658 | | maximum_cardinality_of_expression(B,Props,MB), MaxCard is min(MA,MB). |
| 659 | | maximum_cardinality_of_expression2(set_subtraction(A,_),Props,MaxCard) :- |
| 660 | | maximum_cardinality_of_expression(A,Props,MaxCard). |
| 661 | | maximum_cardinality_of_expression2(cartesian_product(A,B),Props,MaxCard) :- |
| 662 | | maximum_cardinality_of_expression(A,Props,MA), |
| 663 | | maximum_cardinality_of_expression(B,Props,MB), MaxCard is MA*MB. |
| 664 | | maximum_cardinality_of_expression2(identifier(ID),Props,MaxCard) :- % tested in testcase 1917 |
| 665 | | maximum_cardinality_of_identifier(ID,Props,MaxCard). |
| 666 | | maximum_cardinality_of_expression2(empty_set,_,0). |
| 667 | | % TO DO: generalized union... |
| 668 | | %maximum_cardinality_of_expression2(Ex,_) :- nl, print(uncov_max_partition_card(Ex)),nl,nl,fail. |
| 669 | | % TO DO: other cases |
| 670 | | |
| 671 | | maximum_cardinality_of_identifier(ID,Properties,MaxCard) :- |
| 672 | | TID = b(identifier(ID),_,_), |
| 673 | | member_in_conjunction(EqCardExpr,Properties), % look for card(ID) = MaxCard |
| 674 | | is_equality_card_expression(EqCardExpr,Properties,TID,MaxCard), % this is actually an exact card |
| 675 | | !. % TODO: also check for card(ID) <= Bound |
| 676 | | maximum_cardinality_of_identifier(ID,Properties,MaxCard) :- |
| 677 | | get_equality(Properties,ID,EXPR,RestProp), % ID = EXPR, |
| 678 | | % remove equality to avoid loops, TO DO: try several equalities? |
| 679 | | maximum_cardinality_of_expression(EXPR,RestProp,MaxCard). |
| 680 | | maximum_cardinality_of_identifier(ID,Properties,MaxCard) :- |
| 681 | | get_texpr_id(Set,ID), |
| 682 | | get_texpr_expr(C,partition(Set,DisjSets)), % partition(ID, DisjSets) |
| 683 | | select_member_in_conjunction(C,Properties,RestProp), % remove partition to avoid loops |
| 684 | | max_card_of_partition_list(DisjSets,RestProp,0,MaxCard). |
| 685 | | |
| 686 | | % determine maximum cardinality of partition RHS-List (partition(Set,RHS-List)) |
| 687 | | max_card_of_partition_list([],_,Acc,Acc). |
| 688 | | max_card_of_partition_list([TPartSet|T],Properties,Acc,Res) :- |
| 689 | | maximum_cardinality_of_expression(TPartSet,Properties,Max),!, |
| 690 | | NAcc is Acc+Max, |
| 691 | | max_card_of_partition_list(T,Properties,NAcc,Res). |
| 692 | | |
| 693 | | |
| 694 | | :- dynamic find_minimum_cardinality_cache/2. |
| 695 | | find_minimum_cardinality(GS,MC) :- |
| 696 | | find_minimum_cardinality_cache(GS,Nr),!, %print(min_card_cache(GS,Nr)),nl, |
| 697 | | % avoid loops; e.g., if we have N1 >->> N2 and N2 >->> N1 |
| 698 | | number(Nr), |
| 699 | | MC=Nr. |
| 700 | | find_minimum_cardinality(GlobalSetName,Res) :- % print(find_min_card(GlobalSetName)),nl, |
| 701 | | /* try to find out from the Properties whether there is an implied minimum cardinality */ |
| 702 | | b_get_machine_constants(Csts), |
| 703 | | b_get_properties_from_machine(Properties), |
| 704 | | assertz(find_minimum_cardinality_cache(GlobalSetName,unknown)), |
| 705 | | findall(MinCard2,find_minimum_cardinality2(Csts,Properties,GlobalSetName,MinCard2),List), |
| 706 | | debug_println(9,mincard_list(GlobalSetName,List)), |
| 707 | | max_member(MinCard,List), % we do not allow empty global sets anymore, allow_empty_global_sets is obsolete |
| 708 | | inf_greater(MinCard,0), |
| 709 | | retract(find_minimum_cardinality_cache(GlobalSetName,_)), |
| 710 | | assertz(find_minimum_cardinality_cache(GlobalSetName,MinCard)), |
| 711 | | Res=MinCard. |
| 712 | | |
| 713 | | find_minimum_cardinality2(_,Properties,GlobalSetName,MinCard) :- |
| 714 | | member_in_conjunction(C,Properties), |
| 715 | | find_minimum_cardinality3(C,Properties,GlobalSetName,MinCard). |
| 716 | | find_minimum_cardinality2(_Constants,_Properties,GlobalSetName,MinCard) :- |
| 717 | | %print(trying_find_min_card(GlobalSetName,Constants)),nl, |
| 718 | | b_get_disjoint_constants_of_type(GlobalSetName, DisjointConstants,_), |
| 719 | | length(DisjointConstants,MinCard), inf_greater(MinCard,1). |
| 720 | | |
| 721 | | |
| 722 | | :- use_module(partition_detection,[is_disjoint_pred/4]). |
| 723 | | |
| 724 | | find_minimum_cardinality3(C,Properties,GlobalSetName,MinCard) :- |
| 725 | | % check if the PROPERTIES contain an expression of the form card(GS) >= Nr or similar |
| 726 | | is_greaterequalthan_card_global_set(C,GlobalSetName,Properties,MinCard). |
| 727 | | find_minimum_cardinality3(C,Properties,GlobalSetName,MinCard) :- |
| 728 | | % find things like partition(GlobalSetName,S1,...,SN) |
| 729 | | get_texpr_expr(C,partition(Set,DisjSets)), |
| 730 | | global_set_identifier(Set,GlobalSetName), |
| 731 | | % print(partition(Set,DisjSets,GlobalSetName)),nl, |
| 732 | | find_min_card_of_disjoint_sets(DisjSets,Properties,MinCard). % ,print(min(MinCard)),nl. |
| 733 | | find_minimum_cardinality3(C,_Properties,GlobalSetName,MinCard) :- |
| 734 | | get_texpr_expr(C,member(_Something,SURJ)), |
| 735 | | is_surjection(SURJ,Set,OTHERSET), |
| 736 | | global_set_identifier(Set,GlobalSetName), |
| 737 | | \+ global_set_identifier(OTHERSET,GlobalSetName), % Surjection on itself; no use in narrowing down cardinality |
| 738 | | (minimum_cardinality(OTHERSET,MinCard) |
| 739 | | -> true |
| 740 | | ; print('*** could not compute mincard: '), print(OTHERSET),nl,fail), |
| 741 | | (MinCard=inf -> add_error(b_global_sets,'Set has to be of infinite cardinality: ',GlobalSetName),fail ; true). |
| 742 | | find_minimum_cardinality3(C,Properties,GlobalSetName,MinCard) :- |
| 743 | | is_disjoint_pred(C,set(global(GlobalSetName)),A,B), % A /\ B = {} with A<:GS and B<:GS |
| 744 | | minimum_cardinality_of_set(A,Properties,MinA), number(MinA), inf_greater(MinA,0), |
| 745 | | minimum_cardinality_of_set(B,Properties,MinB), number(MinB), inf_greater(MinB,0), |
| 746 | | inf_add(MinA,MinB,MinCard). |
| 747 | | % TO DO: store information and try to combine with other disjointedness infos |
| 748 | | |
| 749 | | % TO DO: detect that if f is total function from non_empty set to set X, then card(X)>0 |
| 750 | | |
| 751 | | % is a surjective function |
| 752 | | is_surjection(SURJ,FromSet,ToSet) :- get_texpr_expr(SURJ,total_surjection(FromSet,ToSet)). |
| 753 | | is_surjection(SURJ,FromSet,ToSet) :- get_texpr_expr(SURJ,partial_surjection(FromSet,ToSet)). |
| 754 | | is_surjection(SURJ,FromSet,ToSet) :- get_texpr_expr(SURJ,total_bijection(FromSet,ToSet)). % here we know the exact cardinality - TO DO: use this fact |
| 755 | | is_surjection(SURJ,FromSet,ToSet) :- get_texpr_expr(SURJ,partial_bijection(FromSet,ToSet)). |
| 756 | | is_surjection(SURJ,FromSet,ToSet) :- get_texpr_expr(SURJ,total_injection(ToSet,FromSet)). % inverse is surjective |
| 757 | | |
| 758 | | |
| 759 | | |
| 760 | | % compute the minmum cardinality of an expression |
| 761 | | minimum_cardinality(ID,MinCard) :- global_set_identifier(ID,GlobalSet),!, |
| 762 | | (b_extract_fd_type(GlobalSet,LowBnd,UpBnd) |
| 763 | | -> MinCard is 1+UpBnd-LowBnd |
| 764 | | ; print('*** Do not know card of : '), print(GlobalSet),nl, |
| 765 | | % TO DO: do full blown cardinality analysis of deferred sets |
| 766 | | fail). |
| 767 | | minimum_cardinality(b(EXPR,TYPE,_INFO),MinCard) :- |
| 768 | | minimum_cardinality2(EXPR,TYPE,MinCard). |
| 769 | | |
| 770 | | %minimum_cardinality2(A,T,R) :- print(mincard(A,T,R)),nl,fail. |
| 771 | | minimum_cardinality2(bool_set,_,2). |
| 772 | | minimum_cardinality2(cartesian_product(A,B),_,Res) :- minimum_cardinality(A,CA), |
| 773 | | minimum_cardinality(B,CB), kernel_objects:safe_mul(CA,CB,Res). |
| 774 | | minimum_cardinality2(interval(A,B),_,Res) :- |
| 775 | | b_get_properties_from_machine(Properties), |
| 776 | | evaluable_integer_expression(A,Properties,EA), |
| 777 | | evaluable_integer_expression(B,Properties,EB), |
| 778 | | (EB >= EA -> Res is EB+1-EA ; Res=0). |
| 779 | | % TO DO: enumerated sets, ... seq, relations, ... ? |
| 780 | | |
| 781 | | |
| 782 | | find_min_card_of_disjoint_sets([],_Properties,0). |
| 783 | | find_min_card_of_disjoint_sets([H|T],Properties,R) :- |
| 784 | | find_min_card_of_disjoint_sets(T,Properties,TN), |
| 785 | | (minimum_cardinality_of_set(H,Properties,MC) |
| 786 | | -> true %,print(min_card(MC,H)),nl |
| 787 | | ; add_internal_error('Call failed: ',minimum_cardinality_of_set(H,Properties,MC)), MC=1), |
| 788 | | R is TN+MC. |
| 789 | | |
| 790 | | |
| 791 | | minimum_cardinality_of_set(b(Expr,set(Type),_),Properties,MC) :- |
| 792 | | minimum_cardinality_of_set_aux(Expr,Type,Properties,MC). |
| 793 | | |
| 794 | | %minimum_cardinality_of_set_aux(P,Type,_,_MC) :- print(min_card(P,Type)),nl,fail. |
| 795 | | minimum_cardinality_of_set_aux(set_extension(List),GlobalSetName,Properties,MinCard) :- !, |
| 796 | | get_texpr_ids(List,AtomicIDList), |
| 797 | | find_inequal_global_set_identifiers(AtomicIDList,GlobalSetName,Properties,DisjointConstants), |
| 798 | | length(DisjointConstants,MinCard). % , print(disj_set_ext(MinCard,DisjointConstants)),nl,nl. |
| 799 | | minimum_cardinality_of_set_aux(identifier(CST),_Type,_Properties,MinCard) :- |
| 800 | | find_minimum_cardinality_cache(CST,Nr),!, |
| 801 | | number(Nr), |
| 802 | | MinCard=Nr. |
| 803 | | minimum_cardinality_of_set_aux(identifier(CST),Type,Properties,MinCard) :- |
| 804 | | Cst_Template = b(identifier(CST),set(Type),_), |
| 805 | | min_card_of_identifier(Cst_Template,CST,Type,Properties,MC), |
| 806 | | !, |
| 807 | | MinCard=MC, |
| 808 | | assertz(find_minimum_cardinality_cache(CST,MC)). % global sets and constants cannot have the same name |
| 809 | | % TO DO: add more cases ? sequence extension cannot appear |
| 810 | | minimum_cardinality_of_set_aux(_,_,_,0). |
| 811 | | |
| 812 | | % determine minimum cardinality of an identifier; usually a constant (cannot be another global set due to B typing). |
| 813 | | min_card_of_identifier(Cst_Template,_CST,_Type,Properties,MinCard) :- |
| 814 | | member_in_conjunction(EqCardExpr,Properties), % look for card(CstTemplate) = MinCard |
| 815 | | is_equality_card_expression(EqCardExpr,Properties,Cst_Template,MinCard), % this is actually an exact card |
| 816 | | !. |
| 817 | | min_card_of_identifier(_Cst_Template,CST,_Type,Properties,MinCard) :- |
| 818 | | member_in_conjunction(EqCardExpr,Properties), % look for card(CstTemplate) = MinCard |
| 819 | | is_greaterequalthan_card_global_set_id(EqCardExpr,CST,_GS,Properties,MinCard), |
| 820 | | MinCard > 0, % otherwise not helpful; maybe we can find a better bound below |
| 821 | | !. |
| 822 | | min_card_of_identifier(Cst_Template,_,_Type,Properties,MC) :- |
| 823 | | get_texpr_expr(PT,partition(Cst_Template,DisjSets)), |
| 824 | | member_in_conjunction(PT,Properties), |
| 825 | | find_min_card_of_disjoint_sets(DisjSets,Properties,MC),!. |
| 826 | | min_card_of_identifier(Cst_Template,_,Type,Properties,MinCard) :- |
| 827 | | % id1:CST ... idk:CST & all_different(id1,...,idk) => card(CST) >= k |
| 828 | | get_texpr_id(TID,ID), |
| 829 | | get_texpr_expr(EL,member(TID,Cst_Template)), |
| 830 | | findall(ID,member_in_conjunction(EL,Properties),AtomicIDList), |
| 831 | | % find all identifiers such that we have id : CST |
| 832 | | AtomicIDList \= [], |
| 833 | | !, % now detect which of these can be inferred to be disjoint |
| 834 | | find_inequal_global_set_identifiers(AtomicIDList,Type,Properties,DisjointConstants), |
| 835 | | length(DisjointConstants,MinCard). |
| 836 | | min_card_of_identifier(Cst_Template,_,_Type,Properties,MC) :- % x:CST => card(CST) >= 1 |
| 837 | | get_texpr_expr(EL,member(_,Cst_Template)), |
| 838 | | member_in_conjunction(EL,Properties), |
| 839 | | !, |
| 840 | | MC=1. |
| 841 | | min_card_of_identifier(Cst_Template,_,_Type,Properties,MC) :- % ID /= {} => card(ID) >= 1 |
| 842 | | member_in_conjunction(Pred,Properties), |
| 843 | | not_empty(Pred,Cst_Template), |
| 844 | | !, |
| 845 | | MC=1. |
| 846 | | |
| 847 | | not_empty(b(E,pred,_),A) :- |
| 848 | | not_empty_aux(E,A). |
| 849 | | not_empty_aux(not_equal(A,B),R) :- |
| 850 | | (definitely_empty_set(B) -> R=A ; definitely_empty_set(A),R=B). |
| 851 | | not_empty_aux(negation(b(equal(A,B),pred,_)),R) :- not_empty_aux(not_equal(A,B),R). |
| 852 | | % card() >= dealt with above |
| 853 | | % not_empty_aux(member(_,TotalFunction)) x: Dom --> R & Dom /= {} ---> R /={} |
| 854 | | |
| 855 | | |
| 856 | | |
| 857 | | |
| 858 | | % DETECT ENUMERATED constants part of a deferred set |
| 859 | | % the following could be further improved |
| 860 | | % try and find out how many different identifiers are in a list in the context of a predicate Prop |
| 861 | | % it is also called by b_get_disjoint_constants_of_type_calc in bmachine |
| 862 | | %find_inequal_global_set_identifiers(L,GS,_P,_R) :- print(find_inequal_global_set_identifiers(L,GS)),nl,fail. |
| 863 | | |
| 864 | | :- use_module(bsyntaxtree,[conjunction_to_list/2]). |
| 865 | | find_inequal_global_set_identifiers(IDS,GS,Prop,Res) :- |
| 866 | | sort(IDS,SIDS), |
| 867 | | conjunction_to_list(Prop,Preds), |
| 868 | | find_inequal_global_set_identifiers1(SIDS,GS,Preds,Res). |
| 869 | | find_inequal_global_set_identifiers1(SIDS,GS,Preds,Res) :- |
| 870 | | length(SIDS,Card), Card>50, |
| 871 | | % try find all_different predicate first, especially for very large deferred sets the code below is slow |
| 872 | | member(b(C,pred,_),Preds), |
| 873 | | all_different(C,List1,Card,GS,Preds), % we have found an all_different predicate of the right size |
| 874 | | get_texpr_ids(List1,L1), |
| 875 | | sort(L1,SIDS),!, % the all_different predicate matches all our identifiers |
| 876 | | debug_println(9,detected_all_diff_global_set(GS,Card)), |
| 877 | | Res=SIDS. |
| 878 | | find_inequal_global_set_identifiers1(SIDS,GS,Preds,Res) :- |
| 879 | | %format('Finding enumerated elements of ~w (over ~w) in: ',[GS,IDS]),translate:print_bexpr(Prop),nl, |
| 880 | | % first apply nested partition predicates as much as possible to obtain max. large partitions of distinct elements |
| 881 | | get_relevant_partition_preds(Preds,GS,RelevantPartitionPreds), % TO DO: cache this computation |
| 882 | | findall(TP,find_a_transitive_partition(RelevantPartitionPreds,TP),TransitivePartitions), |
| 883 | | append(TransitivePartitions,Preds,Preds2), |
| 884 | | add_true(SIDS,TIDS),ord_list_to_avl(TIDS,IDS_AVL), |
| 885 | | find_inequal_global_set_identifiers2(SIDS,IDS_AVL,GS,Preds2,Res). |
| 886 | | |
| 887 | | % utility to be able to call list_to_avl |
| 888 | | add_true([],[]). |
| 889 | | add_true([H|T],[H-true|TT]) :- add_true(T,TT). |
| 890 | | |
| 891 | | |
| 892 | | find_inequal_global_set_identifiers2([],_,_,_,[]). |
| 893 | | find_inequal_global_set_identifiers2([ID|T],AVL,GS,Preds,Res) :- |
| 894 | | (atomic(ID) -> true |
| 895 | | ; add_internal_error('Not atomic id: ',find_inequal_global_set_identifiers([ID|T],GS,Preds,Res)),fail), |
| 896 | | avl_delete(ID,AVL,_,AVL1), |
| 897 | | findall(ID2, (find_inequality(GS,Preds,ID,ID2), |
| 898 | | avl_fetch(ID2, AVL1,_) |
| 899 | | ), DiffIDs), |
| 900 | | sort(DiffIDs,SortedDiffIDs), % also remove duplicates |
| 901 | | (SortedDiffIDs=[],T=[_,_|_] % then try to proceed with T, ignoring ID |
| 902 | | -> find_inequal_global_set_identifiers2(T,AVL1,GS,Preds,Res) |
| 903 | | ; add_true(SortedDiffIDs,SIDT),ord_list_to_avl(SIDT,AVL2), |
| 904 | | Res = [ID|RT], |
| 905 | | find_inequal_global_set_identifiers2(SortedDiffIDs,AVL2,GS,Preds,RT) |
| 906 | | ). |
| 907 | | |
| 908 | | %find_inequality(_,_,ID,ID2) :- % TODO: make usable; currently VALUES clause is precompiled after this is used |
| 909 | | % b_extract_values_clause_assignment(ID,Type,TVal1), |
| 910 | | % b_extract_values_clause_assignment(ID2,Type,TVal2), |
| 911 | | % ID \= ID2, TVal1 \= TVal2. |
| 912 | | find_inequality(GS,Preds,ID,ID2) :- |
| 913 | | member(Conjunct,Preds), %print(chck(C)),nl, |
| 914 | | %translate:print_bexpr_with_limit(Conjunct,100),debug:nl_time, |
| 915 | | inequality(Conjunct,GS,ID,ID2,Preds). |
| 916 | | |
| 917 | | |
| 918 | | get_relevant_partition_preds(Preds,GS,SortedRelevantPartitionPreds) :- |
| 919 | | % detect which partition predicates are relevant for this global set |
| 920 | | findall(partition(Set,Subsets), |
| 921 | | (member(C,Preds), |
| 922 | | is_relevant_partition(C,GS,Set,Subsets)), RelevantPartitionPreds), |
| 923 | | sort(RelevantPartitionPreds,SortedRelevantPartitionPreds). |
| 924 | | is_relevant_partition(b(partition(Set,Subsets),pred,_),GS,Set,Subsets) :- |
| 925 | | get_texpr_type(Set,set(GS)). % , translate:print_bexpr(b(partition(Set,Subsets),pred,[])),nl. |
| 926 | | |
| 927 | | % if we have partition(A,[B,C]) and partition(B,[E,F]) --> generate partition(A,[E,F,C]) |
| 928 | | find_a_transitive_partition(Preds,b(partition(A,C),pred,[generated])) :- |
| 929 | | member(partition(A,B),Preds), |
| 930 | | inline_subsets(B,Preds,Inlined,C), Inlined==inlined. |
| 931 | | inline_subsets([],_,_,[]). |
| 932 | | inline_subsets([Subset1|TS],Preds,inlined,Result) :- get_texpr_id(Subset1,SubID), |
| 933 | | %prefix(Pred,[partition(S2,SubList)|Pred2]), |
| 934 | | select(partition(Subset2,SubList),Preds,Pred2), |
| 935 | | get_texpr_id(Subset2,SubID), |
| 936 | | debug_format(19,'Inlining nested partition predicate for set ~w~n',[SubID]), |
| 937 | | !, |
| 938 | | append(SubList,TS,NewList), |
| 939 | | inline_subsets(NewList,Pred2,_,Result). |
| 940 | | inline_subsets([Subset1|TS],Preds,Inlined,[Subset1|TSRes]) :- inline_subsets(TS,Preds,Inlined,TSRes). |
| 941 | | |
| 942 | | |
| 943 | | inequality(b(P,pred,_),GS,ID,OtherID,FullProps) :- |
| 944 | | inequality_aux(P,GS,ID,OtherID,FullProps). |
| 945 | | inequality_aux(not_equal(I1,I2),GS,ID,OtherID,_) :- |
| 946 | | gs_identifier(I1,GS,ID1), gs_identifier(I2,GS,ID2), |
| 947 | | (ID=ID1 -> OtherID=ID2 ; ID=ID2,OtherID=ID1). |
| 948 | | inequality_aux(not_equal(I1,I2),GS,ID,OtherID,_) :- % treat {aa} \= {bb} or more generally {aa,bb} \= {aa,cc} |
| 949 | | % two identifiers of global sets need not be different |
| 950 | | % in order for two set extensions to be inequal |
| 951 | | % first we check if I1, I2 are in fact subsets of global sets |
| 952 | | get_texpr_type(I1,set(global(_))), |
| 953 | | get_texpr_expr(I1,set_extension(IDs1)), % TODO: is there something more general than a set_extension? |
| 954 | | get_texpr_expr(I2,set_extension(IDs2)), % should full sets or set identifiers added? |
| 955 | | % remove the common elements and see if we find a pair of identifiers |
| 956 | | maplist(remove_all_infos_and_ground,IDs1,IDs1NoInfos), |
| 957 | | maplist(remove_all_infos_and_ground,IDs2,IDs2NoInfos), |
| 958 | | % TODO: maybe it is same to assume the order? |
| 959 | | list_to_ord_set(IDs1NoInfos,IDs1Set), list_to_ord_set(IDs2NoInfos,IDs2Set), |
| 960 | | ord_subtract(IDs1Set,IDs2Set,[E1]), % each set has one identifier that is not in the other one |
| 961 | | ord_subtract(IDs2Set,IDs1Set,[E2]), |
| 962 | | E1 \= E2, % and they are not the same |
| 963 | | gs_identifier(E1,GS,ID1), gs_identifier(E2,GS,ID2), |
| 964 | | set_ids(ID1,ID2,ID,OtherID). |
| 965 | | inequality_aux(negation( b(equal(I1,I2),pred,_)),GS,ID,OtherID,_) :- |
| 966 | | gs_identifier(I1,GS,ID1), gs_identifier(I2,GS,ID2), |
| 967 | | set_ids(ID1,ID2,ID,OtherID). |
| 968 | | inequality_aux(partition(Set,DisjSets),GS,ID,OtherID,Preds) :- |
| 969 | | get_texpr_type(Set,set(GS)), % only look at relevant partitions of correct type |
| 970 | | %nl,print(part(_Set,DisjSets,GS,ID,OtherID)),nl, |
| 971 | | % detect such things as partition(_,{...ID...},...{...OtherID...}...) |
| 972 | | select(Set1,DisjSets,Rest), set_extension_list(Set1,Preds,List1), |
| 973 | | member(O1,List1), gs_identifier(O1,GS,ID), |
| 974 | | member(Set2,Rest), set_extension_list(Set2,Preds,List2), |
| 975 | | member(O2,List2), gs_identifier(O2,GS,OtherID). |
| 976 | | inequality_aux(member(I1,SetDiff),GS,ID,OtherID,_Preds) :- |
| 977 | | % detect ID : SUPERSET - { ... OtherID ...} => ID:SUPERSET & ID /= OtherID |
| 978 | | get_texpr_expr(SetDiff,set_subtraction(_SuperSet,b(OtherSet,_,_))), |
| 979 | | gs_identifier(I1,GS,ID1), |
| 980 | | id_element_of_set(OtherSet,GS,ID2), |
| 981 | | set_ids(ID1,ID2,ID,OtherID). |
| 982 | | inequality_aux(AllDiff,GS,ID,OtherID,Preds) :- |
| 983 | | all_different(AllDiff,List1,_Card,GS,Preds), |
| 984 | | select(O1,List1,RestList), gs_identifier(O1,GS,ID), |
| 985 | | member(O2,RestList), gs_identifier(O2,GS,OtherID). |
| 986 | | |
| 987 | | set_ids(ID1,ID2,ID,OtherID) :- (ID=ID1 -> OtherID=ID2 ; ID=ID2,OtherID=ID1). |
| 988 | | |
| 989 | | id_element_of_set(set_extension(List),GS,ID) :- |
| 990 | | member(I2,List), |
| 991 | | gs_identifier(I2,GS,ID). |
| 992 | | |
| 993 | | % detect card({x1,....,xn}) = n as an all-different predicate |
| 994 | | all_different(equal(A,B),List,Card,Type,Preds) :- |
| 995 | | all_diff_aux(A,B,List,Card,Type,Preds) ; all_diff_aux(B,A,List,Card,Type,Preds). |
| 996 | | all_diff_aux(A,B,List1,Card,Type,Preds) :- |
| 997 | | A=b(card(Set1),_,_), |
| 998 | | get_texpr_type(Set1,set(Type)), % TO DO: also match seq type ? (in case we use predicate in other context) |
| 999 | | set_extension_list(Set1,Preds,List1), |
| 1000 | | evaluable_integer_expression(B,b(truth,pred,[]),Card), |
| 1001 | | length(List1,Card). |
| 1002 | | |
| 1003 | | set_extension_list(b(set_extension(List),_,_),_,List). |
| 1004 | | set_extension_list(b(identifier(ID),_,_),Preds,List) :- |
| 1005 | | % accept card(GS) = nr & ... GS = {....} |
| 1006 | | member(Eq,Preds), |
| 1007 | | equality(Eq,TID,b(set_extension(List),_,_)), |
| 1008 | | get_texpr_id(TID,ID). |
| 1009 | | |
| 1010 | | equality(b(equal(LHS,RHS),pred,_),L,R) :- ( (L,R)=(LHS,RHS) ; (L,R)=(RHS,LHS) ). |
| 1011 | | |
| 1012 | | gs_identifier(b(identifier(ID),GS,_),GS,ID). |
| 1013 | | |
| 1014 | | :- assert_must_succeed(( b_global_sets:is_equality_card_global_set(b(equal(b(card(b(identifier('FACES'),set(global('FACES')),[])),integer,[]),b(integer(6),integer,[])),pred,[]),GS,[],Card),GS='FACES',Card=6) |
| 1015 | | ). |
| 1016 | | :- assert_must_succeed(( b_global_sets:is_equality_card_global_set(b(equal(b(card(b(value(global_set('TITLE')),set(global('TITLE')),[])),integer,[]),b(integer(4),integer,[])),pred,[]),GS,[],Card), |
| 1017 | | GS=='TITLE', Card=4) |
| 1018 | | ). |
| 1019 | | |
| 1020 | | is_equality_card_expression(TE,Properties,Expr,Card) :- |
| 1021 | | get_texpr_expr(TE,equal(LHS,RHS)), |
| 1022 | | get_texpr_expr(LHS,L), get_texpr_expr(RHS,R), |
| 1023 | | ( L=card(Expr), evaluable_integer_expression(R,Properties,Card) |
| 1024 | | ; R=card(Expr), evaluable_integer_expression(L,Properties,Card)). |
| 1025 | | |
| 1026 | | is_equality_card_global_set(TE,GlobalSet,Properties,Card) :- |
| 1027 | ? | is_equality_card_expression(TE,Properties,Expr,Card), |
| 1028 | | global_set_identifier(Expr,GlobalSet), |
| 1029 | | get_texpr_type(Expr,set(global(GlobalSet))). |
| 1030 | | |
| 1031 | | % detect a predicate that stipulates that given Set is infinite |
| 1032 | | % i.e., we detect not(finite(GS)) which can be rewritten from not(GS:FIN(GS)) in ast_cleanup by introduce_finite rule |
| 1033 | | is_infinite_global_set_pred(Pred,GlobalSetName) :- |
| 1034 | | get_texpr_expr(Pred,negation(Pred2)), |
| 1035 | | get_texpr_expr(Pred2,finite(ID)), |
| 1036 | | get_texpr_id(ID,GlobalSetName). |
| 1037 | | |
| 1038 | | %evaluable_integer_expression(X,_Y,_) :- print(ev(X)),nl,fail. |
| 1039 | | % TO DO: maybe we should do some kind of constant folding or call b_compile ?? |
| 1040 | | evaluable_integer_expression(b(E,integer,_),Properties,R) :- |
| 1041 | | evaluable_integer_expression(E,Properties,R). |
| 1042 | | evaluable_integer_expression(max_int,_,R) :- preferences:preference(maxint,R). |
| 1043 | | evaluable_integer_expression(min_int,_,R) :- preferences:preference(minint,R). |
| 1044 | | evaluable_integer_expression(identifier(ID),Properties,R) :- |
| 1045 | | get_texpr_expr(Eq,equal(LHS,RHS)), get_texpr_id(LHS,ID), |
| 1046 | | select_member_in_conjunction(Eq,Properties,RestProp), % avoids loops; but an identifier cannot be used multiple times N+N |
| 1047 | | evaluable_integer_expression(RHS,RestProp,R). |
| 1048 | | evaluable_integer_expression(card(Set),Properties,Card) :- |
| 1049 | | Set = b(identifier(GlobalSetName),set(global(GlobalSetName)),_), |
| 1050 | | (extract_setsize_from_machine_cache(GlobalSetName,L,U) |
| 1051 | | -> Card is U+1-L |
| 1052 | | ; select_member_in_conjunction(Eq,Properties,RestProp), |
| 1053 | | is_equality_card_global_set(Eq,GlobalSetName,RestProp,Card) |
| 1054 | | ). |
| 1055 | | evaluable_integer_expression(integer(Card),_,Card). |
| 1056 | | evaluable_integer_expression(div(A,B),Properties,Res) :- |
| 1057 | | evaluable_integer_expression(A,Properties,RA), |
| 1058 | | evaluable_integer_expression(B,Properties,RB), Res is RA // RB. |
| 1059 | | evaluable_integer_expression(multiplication(A,B),Properties,Res) :- |
| 1060 | | evaluable_integer_expression(A,Properties,RA), |
| 1061 | | evaluable_integer_expression(B,Properties,RB), Res is RA*RB. |
| 1062 | | evaluable_integer_expression(add(A,B),Properties,Res) :- |
| 1063 | | evaluable_integer_expression(A,Properties,RA), |
| 1064 | | evaluable_integer_expression(B,Properties,RB), Res is RA+RB. |
| 1065 | | evaluable_integer_expression(minus(A,B),Properties,Res) :- |
| 1066 | | evaluable_integer_expression(A,Properties,RA), |
| 1067 | | evaluable_integer_expression(B,Properties,RB), Res is RA-RB. |
| 1068 | | evaluable_integer_expression(unary_minus(A),Properties,Res) :- |
| 1069 | | evaluable_integer_expression(A,Properties,RA), Res is 0-RA. |
| 1070 | | |
| 1071 | | % checks if we have an identifier or a precompiled global_set value |
| 1072 | | global_set_identifier(C,GS) :- get_texpr_expr(C,BE), global_set_identifier2(BE,GS). |
| 1073 | | global_set_identifier2(identifier(GlobalSet),GlobalSet). |
| 1074 | | global_set_identifier2(value(global_set(GlobalSet)),GlobalSet). % generated by Z |
| 1075 | | |
| 1076 | | % check for card(GlobalSet) >= Card or similar |
| 1077 | | is_greaterequalthan_card_global_set(TE,GlobalSet,Properties,Card) :- |
| 1078 | | is_greaterequalthan_card_global_set_id(TE,GlobalSet,GlobalSet,Properties,Card). |
| 1079 | | % check for card(Identifier) >= Card or similar with Identifier of type GlobalSet |
| 1080 | | is_greaterequalthan_card_global_set_id(TE,Identifier,GlobalSet,Properties,Card) :- |
| 1081 | | (get_texpr_expr(TE,greater_equal(LHS,RHS)) ; |
| 1082 | | get_texpr_expr(TE,less_equal(RHS,LHS)) ), |
| 1083 | | get_texpr_expr(LHS,card(C)), |
| 1084 | | get_minimum_or_exact_value(RHS,Properties,Card), |
| 1085 | | global_set_identifier(C,Identifier), |
| 1086 | | get_texpr_type(C,set(global(GlobalSet))). |
| 1087 | | is_greaterequalthan_card_global_set_id(TE,Identifier,GlobalSet,Properties,Card1) :- |
| 1088 | | (get_texpr_expr(TE,greater(LHS,RHS)) ; |
| 1089 | | get_texpr_expr(TE,less(RHS,LHS)) ), |
| 1090 | | get_texpr_expr(LHS,card(C)), |
| 1091 | | get_minimum_or_exact_value(RHS,Properties,Card), |
| 1092 | | number(Card), Card1 is Card+1, |
| 1093 | | global_set_identifier(C,Identifier), |
| 1094 | | get_texpr_type(C,set(global(GlobalSet))). |
| 1095 | | is_greaterequalthan_card_global_set_id(TE,GlobalSet,GlobalSet,_Properties,2) :- |
| 1096 | | %% preferences:get_preference(allow_empty_global_sets,false), preference no longer exists |
| 1097 | | /* GlobalSet /= { ANY } -> as GlobalSet cannot be empty & as ANY must be well-typed: |
| 1098 | | we need at least one more element of GlobalSet */ |
| 1099 | | get_texpr_expr(TE,not_equal(LHS,RHS)), |
| 1100 | | ( get_texpr_expr(LHS,identifier(GlobalSet)) |
| 1101 | | -> get_texpr_expr(RHS,set_extension([b(_X,global(GlobalSet),_)])) |
| 1102 | | ; get_texpr_expr(RHS,identifier(GlobalSet)), |
| 1103 | | get_texpr_expr(LHS,set_extension([b(_X,global(GlobalSet),_)])) |
| 1104 | | ). |
| 1105 | | %%print(not_equal(GlobalSet,_X)),nl. |
| 1106 | | % not_equal(S_CHEMIN_ID,b(set_extension([b(identifier(C_NULL_CHEMIN_ID),global(S_CHEMIN_ID),[nodeid(pos(35,1,80,27,80,42)),loc(local,emi,concrete_constants)])]),set(global(S_CHEMIN_ID)),[nodeid(pos(34,1,80,26,80,43))])) |
| 1107 | | |
| 1108 | | % get mininimum or exact value for an expression |
| 1109 | | get_minimum_or_exact_value(TE,Properties,N) :- %print(get_min(TE,Properties)),nl,nl, |
| 1110 | | get_texpr_expr(TE,E), |
| 1111 | | get_minimum_or_exact_value_aux(E,Properties,N). |
| 1112 | | get_minimum_or_exact_value_aux(integer(N),_,N). |
| 1113 | | get_minimum_or_exact_value_aux(identifier(ID),Properties,N) :- |
| 1114 | | %(extract_min_or_exact_value_for_id(ID,Properties,N) -> true). % avoid backtracking; TO DO: in future all this should be part of constraint solving |
| 1115 | | findall(NVal,extract_min_or_exact_value_for_id(ID,Properties,NVal),NVals), |
| 1116 | | max_member(N,NVals). % find the maximum value for which we need to be greater(_equal) to |
| 1117 | | |
| 1118 | | get_minimum_or_exact_value_aux(add(A,B),Properties,Res) :- |
| 1119 | | get_minimum_or_exact_value(A,Properties,RA), |
| 1120 | | get_minimum_or_exact_value(B,Properties,RB), Res is RA+RB. |
| 1121 | | % TO DO: other operators |
| 1122 | | get_minimum_or_exact_value_aux(card(Set),_Properties,Card) :- |
| 1123 | | Set = b(identifier(GlobalSetName),set(global(GlobalSetName)),_), |
| 1124 | | try_extract_setsize_from_machine(GlobalSetName,L,U), % will try extraction; unless a cycle is hit |
| 1125 | | Card is U+1-L. |
| 1126 | | |
| 1127 | | extract_min_or_exact_value_for_id(ID,Properties,N) :- |
| 1128 | | member_in_conjunction(TE,Properties), |
| 1129 | | get_texpr_expr(TE,E), |
| 1130 | | get_value_bound(E,ID,N). |
| 1131 | | |
| 1132 | | % equalities already inlined |
| 1133 | | get_value_bound(equal(LHS,RHS),ID,N) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N). |
| 1134 | | get_value_bound(equal(RHS,LHS),ID,N) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N). |
| 1135 | | get_value_bound(greater(LHS,RHS),ID,N1) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N), N1 is N+1. |
| 1136 | | get_value_bound(less(RHS,LHS),ID,N1) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N), N1 is N+1. |
| 1137 | | get_value_bound(greater_equal(LHS,RHS),ID,N) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N). |
| 1138 | | get_value_bound(less_equal(RHS,LHS),ID,N) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N). |
| 1139 | | |
| 1140 | | get_texpr_integer(b(integer(N),_,_),N). |
| 1141 | | |
| 1142 | | |
| 1143 | | :- volatile global_set_user_defined_scope/2. |
| 1144 | | :- dynamic global_set_user_defined_scope/2. |
| 1145 | | |
| 1146 | | /* allow to set scope for specific sets, e.g., via command-line */ |
| 1147 | | set_user_defined_scope(GS,X) :- |
| 1148 | | \+ number(X),!, |
| 1149 | | add_internal_error('Scope must be number: ',set_user_defined_scope(GS,X)). |
| 1150 | | set_user_defined_scope(GS,X) :- |
| 1151 | | format('% Setting user defined scope: ~w == ~w~n',[GS,X]), |
| 1152 | | assertz(global_set_user_defined_scope(GS,X)). |
| 1153 | | |
| 1154 | | get_user_defined_scope(GlobalSetName,_,_,_) :- var(GlobalSetName),!, |
| 1155 | | add_internal_error('Arg is variable: ',get_user_defined_scope(GlobalSetName)). |
| 1156 | | get_user_defined_scope(GlobalSetName,LowBound,UpBound,Span) :- |
| 1157 | | (global_set_user_defined_scope(GlobalSetName,UpBound) % from command-line |
| 1158 | | -> LowBound=1, Span = unknown |
| 1159 | | ; extract_setsize_from_defs(GlobalSetName,LowBound,UpBound,Span) % scopeSET == Low..Up from DEFINITIONS |
| 1160 | | ). |
| 1161 | | |
| 1162 | | :- use_module(translate,[translate_bexpression/2]). |
| 1163 | | % find scope_NAME == Low..Up in DEFINITIONS |
| 1164 | | extract_setsize_from_defs(GlobalSetName,LowBound,UpBound,DefTerm) :- |
| 1165 | | b_get_machine_setscope(GlobalSetName,DefTerm), |
| 1166 | | get_texpr_expr(DefTerm,DefExpr), |
| 1167 | | (extract_integer_range(DefExpr,LowBound,UpBound) |
| 1168 | | -> true |
| 1169 | | ; translate_bexpression(DefTerm,DS), |
| 1170 | | add_warning(extract_setsize_from_defs,'scope DEFINITION for deferred set should be number or interval: ',DS,DefTerm), |
| 1171 | | fail |
| 1172 | | ). |
| 1173 | | |
| 1174 | | extract_integer_range(interval(LB,UB), LowBound, UpBound) :- |
| 1175 | | get_texpr_expr(LB,integer(LowBound)), get_texpr_expr(UB,integer(UpBound)). |
| 1176 | | %extract_integer_range(set_extension(CstList),LowBound,UpBound) :- |
| 1177 | | % LowBound = 1, length(CstList,UpBound). % extract names of constants in list for pretty printing; check that different names |
| 1178 | | %extract_integer_range(value(avl_set(A)),LowBound,UpBound) :- |
| 1179 | | % LowBound = 1, avl_size(A,UpBound). |
| 1180 | | extract_integer_range(integer(UpBound),LowBound,UpBound) :- |
| 1181 | | LowBound = 1, UpBound>0. |
| 1182 | | |
| 1183 | | |
| 1184 | | :- dynamic start_extracting_setsize/1. |
| 1185 | | |
| 1186 | | start_extracting_setsize_from_machine(GlobalSetName) :- %print(start(GlobalSetName)),nl, |
| 1187 | | (start_extracting_setsize(GlobalSetName) |
| 1188 | | -> add_internal_error('Cyclic computation: ',start_extracting_setsize_from_machine(GlobalSetName)) |
| 1189 | | ; assertz(start_extracting_setsize(GlobalSetName))). |
| 1190 | | |
| 1191 | | assert_extract_setsize_from_machine_cache(GlobalSetName,LowBound,UpBound) :- |
| 1192 | | retractall(start_extracting_setsize(GlobalSetName)), |
| 1193 | | %print(finished(GlobalSetName,LowBound,UpBound)),nl, |
| 1194 | | (retract(extract_setsize_from_machine_cache(GlobalSetName,L,U)) |
| 1195 | | -> print(overriding_set_size(GlobalSetName,LowBound-UpBound,L-U)),nl |
| 1196 | | ; true), |
| 1197 | | assertz(extract_setsize_from_machine_cache(GlobalSetName,LowBound,UpBound)), |
| 1198 | | (integer(UpBound), UpBound<LowBound |
| 1199 | | -> format_error_with_nl('*** Illegal empty global set ~w, bounds ~w:~w',[GlobalSetName,LowBound,UpBound]) |
| 1200 | | ; true |
| 1201 | | ). |
| 1202 | | |
| 1203 | | |
| 1204 | | %% b_try_update_fd_cardinality has been removed |
| 1205 | | |
| 1206 | | :- volatile enumerated_set/1, fixed_deferred_set_size/2. |
| 1207 | | :- dynamic enumerated_set/1, fixed_deferred_set_size/2. |
| 1208 | | % true if GS is a deferred set whose size was not fixed; i.e., ProB has not inspected all possible instantiations |
| 1209 | | % TODO: rename to unfixed_or_infinite_deferred_set |
| 1210 | | unfixed_deferred_set(GS) :- |
| 1211 | ? | if(b_global_set(GS), |
| 1212 | | (\+ enumerated_set(GS), |
| 1213 | | \+ fixed_finite_deferred_set_size(GS,_)), |
| 1214 | | (debug_println(unknown_global_set(GS)),fail)). % triggered in test 2063 |
| 1215 | | |
| 1216 | ? | unfixed_deferred_set_exists :- b_global_set(GS), unfixed_deferred_set(GS),!. |
| 1217 | | |
| 1218 | | % a global set that is treated as infinite by ProB |
| 1219 | | % not unfixed deferred sets may still in principle allowed to be infinite, but could be give a finite card by ProB |
| 1220 | | infinite_global_set(GlobalSetName) :- |
| 1221 | | (fixed_deferred_set_size(GlobalSetName,X) -> X=inf |
| 1222 | | ; inferred_maximum_global_set_cardinality(GlobalSetName,MaxCard) -> MaxCard=inf % should not happen |
| 1223 | | ; enumerated_set(GlobalSetName) -> false |
| 1224 | | % ; (precompilation_phase(Phase) -> true ; Phase=0), |
| 1225 | | % nl,print(not_provably_infinite_global_set(GlobalSetName,Phase)),nl,nl,portray_global_sets,nl,fail |
| 1226 | | ). |
| 1227 | | |
| 1228 | | provably_finite_global_set(GlobalSetName) :- |
| 1229 | | enumerated_set(GlobalSetName),!. |
| 1230 | | provably_finite_global_set(GlobalSetName) :- |
| 1231 | | fixed_deferred_set_size(GlobalSetName,X),!, number(X). |
| 1232 | | provably_finite_global_set(GlobalSetName) :- |
| 1233 | | inferred_maximum_global_set_cardinality(GlobalSetName,MaxCard),!, |
| 1234 | | integer(MaxCard). |
| 1235 | | %provably_finite_global_set(GS) :- (precompilation_phase(Phase) -> true ; Phase=0), |
| 1236 | | % nl,print(not_provably_finite_global_set(GS,Phase)),nl,nl,portray_global_sets,nl,fail. |
| 1237 | | |
| 1238 | | fixed_finite_deferred_set_size(Set,Card) :- fixed_deferred_set_size(Set,Card), integer(Card). |
| 1239 | | |
| 1240 | | contains_unfixed_deferred_set(Type) :- (contains_unfixed_deferred_set(Type,_) -> true). |
| 1241 | | |
| 1242 | | contains_unfixed_deferred_set(global(Type),DS) :- unfixed_deferred_set(Type),DS=Type. |
| 1243 | | contains_unfixed_deferred_set(set(Type),DS) :- contains_unfixed_deferred_set(Type,DS). |
| 1244 | | contains_unfixed_deferred_set(seq(Type),DS) :- contains_unfixed_deferred_set(Type,DS). |
| 1245 | | contains_unfixed_deferred_set(couple(Type1,Type2),DS) :- |
| 1246 | | (contains_unfixed_deferred_set(Type1,DS) -> true ; contains_unfixed_deferred_set(Type2,DS)). |
| 1247 | | contains_unfixed_deferred_set(record(Fields),DS) :- |
| 1248 | | (member(field(_,Type),Fields), contains_unfixed_deferred_set(Type,DS) -> true). |
| 1249 | | |
| 1250 | | |
| 1251 | | % enumerated sets can be registered here, before cleaning up a machine |
| 1252 | | % this way we already know which sets are enumerated and definitely finite |
| 1253 | | % and we can also register the enumerated set elements |
| 1254 | | register_enumerated_sets([],_). |
| 1255 | | register_enumerated_sets([TID|T],Elems) :- def_get_texpr_id(TID,ID), get_texpr_type(TID,set(Type)), |
| 1256 | | El = b(identifier(Elem),Type,_), |
| 1257 | | findall(Elem,member(El,Elems),EnumElems), |
| 1258 | | pre_register_enumerated_set_with_elems(ID,EnumElems), |
| 1259 | | register_enumerated_sets(T,Elems). |
| 1260 | | |
| 1261 | | pre_register_enumerated_set_with_elems(GlobalSetName,ListOfConstants) :- |
| 1262 | | (enumerated_set(GlobalSetName) -> true |
| 1263 | | ; debug_format(19,'Pre-register ~w = ~w~n',[GlobalSetName,ListOfConstants]), |
| 1264 | | assertz(enumerated_set(GlobalSetName)), |
| 1265 | | LowBound=1, |
| 1266 | | add_named_constants_to_global_set(GlobalSetName,LowBound,ListOfConstants) % probably only lookup_global_constant relevant |
| 1267 | | ). |
| 1268 | | % pre-registering just the enumerated set name |
| 1269 | | pre_register_enumerated_set(GlobalSetName) :- |
| 1270 | | (enumerated_set(GlobalSetName) -> true |
| 1271 | | ; debug_format(9,'Pre-register ~w~n',[GlobalSetName]), |
| 1272 | | assertz(enumerated_set(GlobalSetName)) |
| 1273 | | ). |
| 1274 | | |
| 1275 | | |
| 1276 | | :- assert_pre(b_global_sets:assert_enumerated_set(_GS,_L,_,_),true). |
| 1277 | | % TODO(DP, 7.8.2008) |
| 1278 | | % (atomic(GS),type_check(L,list(xml_identifier)))). |
| 1279 | | :- assert_post(b_global_sets:assert_enumerated_set(_,_,L,U),(number(L),number(U))). |
| 1280 | | |
| 1281 | | % TODO: also process b_extract_values_clause_assignment(Cst,integer,Nr) facts ! |
| 1282 | | assert_enumerated_set(GlobalSetName,ListOfConstants,LowBound,UpBound) :- |
| 1283 | | pre_register_enumerated_set(GlobalSetName), |
| 1284 | | % print_message(enum_set(GlobalSetName,ListOfConstants)), |
| 1285 | | LowBound = 1, |
| 1286 | | length(ListOfConstants,UpBound), |
| 1287 | | assert_extract_setsize_from_machine_cache(GlobalSetName,LowBound,UpBound), |
| 1288 | | add_named_constants_to_global_set(GlobalSetName,LowBound,ListOfConstants). |
| 1289 | | |
| 1290 | | add_named_constants_to_global_set(GlobalSetName,LowBound,ListOfConstants) :- |
| 1291 | | (b_global_constant(GlobalSetName,CurNr,CurCst) |
| 1292 | | -> add_error_fail(add_named_constants_to_global_set,'Globalset already has constants: ', |
| 1293 | | b_global_constant(GlobalSetName,CurNr,CurCst)) |
| 1294 | | ; true), |
| 1295 | ? | nth0(Nr0,ListOfConstants,Cst), |
| 1296 | | (lookup_global_constant(Cst,fd(_,GS2)) |
| 1297 | | -> (GS2=GlobalSetName |
| 1298 | | -> add_internal_error('Duplicate element in global set:',(Cst,GS2)) % error is already caught by type checker |
| 1299 | | ; add_internal_error('Element appears in multiple sets:',(Cst,GS2,GlobalSetName)) % ditto |
| 1300 | | ) |
| 1301 | | ; true |
| 1302 | | ), |
| 1303 | | Nr is Nr0+LowBound, |
| 1304 | | assertz(b_global_constant(GlobalSetName,Nr,Cst)), |
| 1305 | | term_hash(fd(Nr,GlobalSetName),Hash), |
| 1306 | | assertz(b_global_constant_hash(Hash,GlobalSetName,Nr,Cst)), |
| 1307 | | assertz(lookup_global_constant(Cst,fd(Nr,GlobalSetName))), |
| 1308 | | %format('Added ~w as nr ~w to ~w~n',[Cst,Nr,GlobalSetName]), |
| 1309 | | fail. |
| 1310 | | add_named_constants_to_global_set(_,_,_). |
| 1311 | | |
| 1312 | | |
| 1313 | | %add_global_set_with_named_constants(GlobalSetName,ListOfConstants) :- |
| 1314 | | % (b_global_sets:assert_enumerated_set(GlobalSetName,ListOfConstants,_,_)), |
| 1315 | | % add_new_global_set(GlobalSetName). |
| 1316 | | |
| 1317 | | |
| 1318 | | |
| 1319 | | :- assert_pre(b_global_sets:b_extract_fd_type(_G,_L,_U),true). |
| 1320 | | :- assert_post(b_global_sets:b_extract_fd_type(G,L,U),(atomic(G),(integer(L),integer(U)))). |
| 1321 | | |
| 1322 | | b_extract_fd_type(GlobalSetName,LowBound,UpBound) :- |
| 1323 | ? | b_global_set(GlobalSetName), % was b_get_machine_set(GlobalSetName), |
| 1324 | | extract_setsize_from_machine(GlobalSetName,LowBound,UpBound). |
| 1325 | | |
| 1326 | | is_b_precompiled_globalset(GlobalSetName) :- extract_setsize_from_machine_cache(GlobalSetName,_,_). |
| 1327 | | |
| 1328 | | % like b_extract_fd_type above, but with GS known (avoids backtracking b_supplementary_global_set) |
| 1329 | | b_get_fd_type_bounds(NonvarGlobalSetname,LowBound,UpBound) :- nonvar(NonvarGlobalSetname), |
| 1330 | | extract_setsize_from_machine_cache(NonvarGlobalSetname,L,U), |
| 1331 | | !, |
| 1332 | | (LowBound,UpBound)=(L,U). |
| 1333 | | b_get_fd_type_bounds(GS,L,U) :- b_replaced_global_set(GS,_),!, |
| 1334 | | add_internal_error('Global set has been replaced: ',b_get_fd_type_bounds(GS,L,U)),fail. |
| 1335 | | b_get_fd_type_bounds(GS,L,U) :- |
| 1336 | | add_internal_error('Illegal call: ',b_get_fd_type_bounds(GS,L,U)),fail. |
| 1337 | | |
| 1338 | | |
| 1339 | | b_global_set_cardinality('STRING',Card) :- !, Card=inf. |
| 1340 | | b_global_set_cardinality('REAL',Card) :- !, Card=inf. |
| 1341 | | b_global_set_cardinality('FLOAT',Card) :- !, Card=18446744073709551616. % 2^64 - TO DO: check |
| 1342 | | b_global_set_cardinality('NAT',Card) :- !, get_preference(maxint,MaxInt), Card is MaxInt+1. |
| 1343 | | b_global_set_cardinality('NATURAL',Card) :- !, Card=inf. %get_preference(maxint,MaxInt), Card is MaxInt+1. |
| 1344 | | b_global_set_cardinality('NAT1',Card) :- !, get_preference(maxint,Card). |
| 1345 | | b_global_set_cardinality('NATURAL1',Card) :- !, Card=inf. % get_preference(maxint,Card). |
| 1346 | | b_global_set_cardinality('INT',Card) :- !, get_preference(maxint,MaxInt), |
| 1347 | | get_preference(minint,MinInt), |
| 1348 | | (MaxInt >= MinInt -> Card is MaxInt-MinInt+1 |
| 1349 | | ; add_error(b_global_set_cardinality,'MININT larger than MAXINT',(MinInt:MaxInt)), |
| 1350 | | Card = 0 |
| 1351 | | ). |
| 1352 | | b_global_set_cardinality('INTEGER',Card) :- !, Card=inf. % b_global_set_cardinality('INT',Card). |
| 1353 | | b_global_set_cardinality(GlobalSet,Card) :- nonvar(GlobalSet),!, |
| 1354 | | (b_fd_card(GlobalSet,C) -> Card=C |
| 1355 | | ; b_replaced_global_set(GlobalSet,_) -> |
| 1356 | | add_internal_error('Global set has been replaced: ', b_global_set_cardinality(GlobalSet,Card)), |
| 1357 | | fail |
| 1358 | | ; add_internal_error('Unknown global set: ', b_global_set_cardinality(GlobalSet,Card)), |
| 1359 | | fail). |
| 1360 | | b_global_set_cardinality(GlobalSet,Card) :- %b_global_set(GlobalSet), |
| 1361 | | b_extract_fd_type(GlobalSet,Low,Up), Card is 1+ Up - Low. |
| 1362 | | % enum_warning_for_global_set(GlobalSet,'assuming deferred set to be of given cardinality',Card,trigger_true(card)). |
| 1363 | | %% Note: The Disprover now checks whether unfixed_deferred sets were involved; keeping track of implicit enumerations of deferred sets is just too difficult |
| 1364 | | |
| 1365 | | b_fd_card(GlobalSet,Card) :- b_get_fd_type_bounds(GlobalSet,Low,Up), |
| 1366 | | (Up=inf -> Card=inf ; Card is 1+Up-Low). |
| 1367 | | |
| 1368 | | b_non_empty_global_set(_). % allow_empty_global_sets no longer exists; all global_sets are non-empty |
| 1369 | | %b_non_empty_global_set(IntSet) :- b_integer_or_real_or_string_set(IntSet). |
| 1370 | | %b_non_empty_global_set(GlobalSet) :- b_global_set(GlobalSet), |
| 1371 | | % b_get_fd_type_bounds(GlobalSet,Low,Up), Up >= Low. |
| 1372 | | |
| 1373 | | b_empty_global_set(_) :- fail. % allow_empty_global_sets no longer exists; all global_sets are non-empty |
| 1374 | | %b_empty_global_set(GlobalSet) :- %b_global_set(GlobalSet), |
| 1375 | | % b_get_fd_type_bounds(GlobalSet,Low,Up), Up < Low. |
| 1376 | | |
| 1377 | | |
| 1378 | | b_integer_or_real_or_string_set('STRING'). |
| 1379 | | b_integer_or_real_or_string_set('FLOAT'). |
| 1380 | | b_integer_or_real_or_string_set('REAL'). |
| 1381 | | b_integer_or_real_or_string_set(X) :- b_integer_set(X). |
| 1382 | | |
| 1383 | | b_integer_or_real_set('REAL'). |
| 1384 | | b_integer_or_real_set('FLOAT'). |
| 1385 | | b_integer_or_real_set(X) :- b_integer_set(X). |
| 1386 | | |
| 1387 | | b_integer_set('NAT'). b_integer_set('NATURAL'). |
| 1388 | | b_integer_set('NAT1'). b_integer_set('NATURAL1'). |
| 1389 | | b_integer_set('INT'). b_integer_set('INTEGER'). |
| 1390 | | |
| 1391 | | % convert a type term into a global_set term |
| 1392 | | try_b_type2global_set(string,R) :- !, R= global_set('STRING'). |
| 1393 | | try_b_type2global_set(integer,R) :- !, R= global_set('INTEGER'). |
| 1394 | | try_b_type2global_set(real,R) :- !, R= global_set('REAL'). |
| 1395 | | try_b_type2global_set(global(GS),R) :- b_global_set(GS),!, R=global_set(GS). |
| 1396 | | try_b_type2global_set(freetype(ID),R) :- !, R=freetype(ID). |
| 1397 | | try_b_type2global_set(boolean,R) :- !, |
| 1398 | | %custom_explicit_sets:construct_avl_from_lists([pred_false /* bool_false */,pred_true /* bool_true */],R). |
| 1399 | | R = avl_set(node(pred_false,true,1,empty,node(pred_true,true,0,empty,empty))). |
| 1400 | | |
| 1401 | | b_type2_set(GS,Res) :- try_b_type2global_set(GS,GR),!, Res=GR. |
| 1402 | | b_type2_set(Type,Res) :- Res = closure(['_zzzz_unary'],[Type],b(truth,pred,[])). |
| 1403 | | %b_type2global_set(GS,_) :- add_error_and_fail(b_type2_set,'Type cannot be converted to global_set: ',GS). |
| 1404 | | |
| 1405 | | :- use_module(fd_utils_clpfd). % in_fd |
| 1406 | | |
| 1407 | | global_type(fd(X,GlobalSet),GlobalSet) :- %b_global_set(GlobalSet), |
| 1408 | | if(b_get_fd_type_bounds(GlobalSet,Low,Up), %print(b_get_fd_type_bounds(X,GlobalSet,Low,Up)),nl, when(nonvar(X),trace), |
| 1409 | | % print(in_fd(X,Low,Up)),nl, |
| 1410 | | in_fd(X,Low,Up), |
| 1411 | | add_internal_error('Illegal global set: ',global_type(fd(X,GlobalSet),GlobalSet))). |
| 1412 | | global_type2(X,GlobalSet) :- %b_global_set(GlobalSet), |
| 1413 | | if(b_get_fd_type_bounds(GlobalSet,Low,Up), |
| 1414 | | in_fd(X,Low,Up), |
| 1415 | | add_internal_error('Illegal global set: ',global_type2(X,GlobalSet))). |
| 1416 | | |
| 1417 | | global_type_wf(fd(X,GlobalSet),GlobalSet,WF) :- %b_global_set(GlobalSet), |
| 1418 | | b_get_fd_type_bounds(GlobalSet,Low,Up), %print(b_get_fd_type_bounds(X,GlobalSet,Low,Up)),nl, |
| 1419 | | in_fd_wf(X,Low,Up,WF). |
| 1420 | | |
| 1421 | | % get value and setup global_type FD constraint if it is a variable |
| 1422 | | get_global_type_value(FD,Type,X) :- |
| 1423 | | (var(FD) % print(fresh(Type,FD)),nl, |
| 1424 | | -> (nonvar(Type) |
| 1425 | | -> global_type2(X,Type) % instantiate FD bounds if we create a fresh FD variable X |
| 1426 | | ; true |
| 1427 | | ), |
| 1428 | | FD = fd(X,Type) |
| 1429 | | ; FD = fd(X,TypeX), |
| 1430 | | (Type \== TypeX, nonvar(Type), nonvar(TypeX) |
| 1431 | | -> add_internal_error('FD Type error:',get_global_type_value(FD,Type,X)) |
| 1432 | | ; true), |
| 1433 | | TypeX=Type |
| 1434 | | ). |
| 1435 | | |
| 1436 | | % like enum_global_type but with a nonvar GlobalSet + generate enum_warning for deferred set (currently commented out) |
| 1437 | | enumerate_global_type_with_enum_warning(R,GlobalSet,EnumWarning,WF) :- var(R),!, % print(enum_var(R,GlobalSet)),nl, |
| 1438 | | b_get_fd_type_bounds_with_enum_warning(X,GlobalSet,Low,Up,EnumWarning,WF), |
| 1439 | | % first setup CLP(FD) bounds before enumerating |
| 1440 | | in_fd(X,Low,Up), % Note: Up can be the restricted range for infinite sets |
| 1441 | | R=fd(X,GlobalSet), |
| 1442 | ? | enum_fd(X,Low,Up). |
| 1443 | | enumerate_global_type_with_enum_warning(fd(X,GlobalSet),GlobalSet,EnumWarning,WF) :- |
| 1444 | | (nonvar(X) -> true |
| 1445 | | ; b_get_fd_type_bounds_with_enum_warning(X,GlobalSet,Low,Up,EnumWarning,WF), |
| 1446 | ? | enum_fd(X,Low,Up) |
| 1447 | | ). |
| 1448 | | |
| 1449 | | :- use_module(probsrc(clpfd_interface),[clpfd_domain/3]). |
| 1450 | | |
| 1451 | | b_get_fd_type_bounds_with_enum_warning(X,GlobalSet,Low2,Up2,EnumWarning,WF) :- |
| 1452 | | b_get_fd_type_bounds(GlobalSet,Low,Up), |
| 1453 | | (Up=inf |
| 1454 | | -> clpfd_domain(X,LowX,UpX), |
| 1455 | | (integer(UpX), integer(LowX) -> Low2=LowX, Up2=UpX |
| 1456 | | ; get_preference(maxint,MaxInt), |
| 1457 | | (integer(LowX), LowX >= MaxInt -> |
| 1458 | | gen_enum_warning_wf(GlobalSet,Low:Up,LowX:LowX,EnumWarning,unknown,WF), |
| 1459 | | Low2=LowX, Up2=LowX % we could increase Up2, e.g., to LowX+MaxInt |
| 1460 | | ; gen_enum_warning_wf(GlobalSet,Low:Up,Low:MaxInt,EnumWarning,unknown,WF), |
| 1461 | | Low2=Low, Up2=MaxInt |
| 1462 | | ) |
| 1463 | | ) |
| 1464 | | ; Low2=Low,Up2=Up |
| 1465 | | ). |
| 1466 | | |
| 1467 | | % enumerate global types, but limit infinite ones |
| 1468 | | enum_global_type_limited(fd(X,GlobalSet),GlobalSet) :- %b_global_set(GlobalSet), |
| 1469 | | (nonvar(GlobalSet) -> |
| 1470 | ? | (b_get_fd_type_bounds_limited(GlobalSet,Low,Up) -> enum_global_type_aux(X,Low,Up,GlobalSet) |
| 1471 | | ; add_internal_error('Illegal value: ',enum_global_type(fd(X,GlobalSet),GlobalSet)),fail) |
| 1472 | | ; b_global_set(GlobalSet), |
| 1473 | | b_get_fd_type_bounds(GlobalSet,Low,Up), |
| 1474 | | enum_global_type_aux(X,Low,Up,GlobalSet) |
| 1475 | | ). |
| 1476 | | |
| 1477 | | enum_global_type_aux(X,Low,Up,GlobalSet) :- |
| 1478 | | atomic(X), |
| 1479 | | !, |
| 1480 | | (number(X),X >= Low, X =< Up |
| 1481 | | -> true |
| 1482 | | ; add_internal_error('Illegal value: ', enum_global_type_aux(X,Low,Up,GlobalSet))). |
| 1483 | | enum_global_type_aux(X,Low,Up,_GlobalSet) :- |
| 1484 | ? | enum_fd(X,Low,Up). |
| 1485 | | |
| 1486 | | % get range information for global set and generate warning if necessary |
| 1487 | | %b_fd_type_with_enum_warning(GlobalSet,Low,Up) :- |
| 1488 | | % b_fd_type(GlobalSet,Low,Up), |
| 1489 | | % enum_warning_for_global_set(GlobalSet,'assuming deferred set to be of finite cardinality',Up,trigger_true(card)). |
| 1490 | | |
| 1491 | | /* ------------------------------------- */ |
| 1492 | | |
| 1493 | | all_elements_of_type(Type,Set) :- |
| 1494 | | all_elements_of_type_wf(Type,Set,no_wf_available). |
| 1495 | | |
| 1496 | | :- block all_elements_of_type_wf(-,?,?). |
| 1497 | | all_elements_of_type_wf(Type,Set,WF) :- % print(all_elements(Type,Set)),nl, |
| 1498 | | all_elements_of_type2(Type,Set,WF). |
| 1499 | | |
| 1500 | | :- use_module(kernel_objects,[all_strings_wf/2, gen_enum_warning_wf/6]). |
| 1501 | | all_elements_of_type2('STRING',Res,WF) :- !, |
| 1502 | | all_strings_wf(Res,WF). |
| 1503 | | all_elements_of_type2('NAT',Res,_WF) :- !, get_preference(maxint,MaxInt), |
| 1504 | | gen_int_set(0,MaxInt,Res). |
| 1505 | | all_elements_of_type2('NATURAL',Res,WF) :- !, get_preference(maxint,MaxInt), |
| 1506 | | gen_enum_warning_wf('NATURAL',0:sup,0:MaxInt,trigger_throw('NATURAL'),unknown,WF), %print('### Warning: expanding NATURAL'),nl, |
| 1507 | | gen_int_set(0,MaxInt,Res). |
| 1508 | | all_elements_of_type2('NAT1',Res,_WF) :- !, get_preference(maxint,MaxInt), |
| 1509 | | gen_int_set(1,MaxInt,Res). |
| 1510 | | all_elements_of_type2('NATURAL1',Res,WF) :- !, |
| 1511 | | get_preference(maxint,MaxInt), |
| 1512 | | gen_enum_warning_wf('NATURAL1',1:sup,1:MaxInt,trigger_throw('NATURAL1'),unknown,WF), %print('### Warning: expanding NATURAL1'),nl, |
| 1513 | | gen_int_set(1,MaxInt,Res). |
| 1514 | | all_elements_of_type2('INT',Res,_WF) :- !, get_preference(maxint,MaxInt), |
| 1515 | | get_preference(minint,MinInt), |
| 1516 | | gen_int_set(MinInt,MaxInt,Res). |
| 1517 | | all_elements_of_type2('INTEGER',Res,WF) :- !, get_preference(maxint,MaxInt), |
| 1518 | | get_preference(minint,MinInt), |
| 1519 | | gen_enum_warning_wf('INTEGER',inf:sup,MinInt:MaxInt,trigger_throw('INTEGER'),unknown,WF), %print('### Warning: expanding INTEGER'),nl, |
| 1520 | | gen_int_set(MinInt,MaxInt,Res). |
| 1521 | | all_elements_of_type2('FLOAT',Res,WF) :- !, all_elements_of_type2('REAL',Res,WF). |
| 1522 | | all_elements_of_type2('REAL',Res,WF) :- !, |
| 1523 | | gen_enum_warning_wf('REAL',inf:sup,0.0:1.0,trigger_throw('REAL'),unknown,WF), |
| 1524 | | Res = [term(floating(0.0)),term(floating(1.0))]. % not sure this will ever be used |
| 1525 | | all_elements_of_type2(Type,Set,WF) :- |
| 1526 | | b_get_fd_type_bounds(Type,Low,Up), |
| 1527 | | (Up=inf |
| 1528 | | -> get_preference(maxint,MaxInt), % infinite deferred set |
| 1529 | | gen_enum_warning_wf(Type,Low:Up,Low:MaxInt,trigger_throw(Type),unknown,WF), |
| 1530 | | findall(fd(El,Type),enum_fd_linear(El,Low,MaxInt),Set) |
| 1531 | | ; % ensure we do not use Random enumeration |
| 1532 | | findall(fd(El,Type),enum_fd_linear(El,Low,Up),Set) |
| 1533 | | ). |
| 1534 | | |
| 1535 | | % a version of all_elements_of_type which may randomise element order if preference set |
| 1536 | | :- block all_elements_of_type_rand_wf(-,?,?). |
| 1537 | | all_elements_of_type_rand_wf(Type,Set,WF) :- % print(all_elements(Type,Set)),nl, |
| 1538 | | all_elements_of_type_rand2(Type,Set,WF). |
| 1539 | | |
| 1540 | ? | all_elements_of_type_rand2(Type,Set,_WF) :- b_global_set(Type),!, |
| 1541 | | b_get_fd_type_bounds(Type,Low,Up), |
| 1542 | | % may use Random enumeration if preference RANDOMISE_ENUMERATION_ORDER set |
| 1543 | | findall(fd(El,Type),enum_fd(El,Low,Up),Set). |
| 1544 | | all_elements_of_type_rand2(Type,Set,WF) :- % TO DO: we could permute the solution here; but currently all_elements_of_type_rand2 is only called for enumerated/deferred sets |
| 1545 | | all_elements_of_type2(Type,Set,WF). |
| 1546 | | |
| 1547 | | |
| 1548 | | gen_int_set(Min,Max,Set) :- |
| 1549 | | (Min>Max -> Set = [] |
| 1550 | | ; Set = [int(Min)|RSet], |
| 1551 | | M1 is Min+1, |
| 1552 | | gen_int_set(M1,Max,RSet)). |
| 1553 | | |
| 1554 | | %% list_contains_unfixed_deferred_set_id(+TypedIds). |
| 1555 | | list_contains_unfixed_deferred_set_id([b(identifier(_),Type,_)|_]) :- |
| 1556 | | contains_unfixed_deferred_set(Type), |
| 1557 | | !. |
| 1558 | | list_contains_unfixed_deferred_set_id([_|T]) :- |
| 1559 | | list_contains_unfixed_deferred_set_id(T). |
| 1560 | | |
| 1561 | | % ----------------- |
| 1562 | | :- public portray_global_sets/0. % debugging utility |
| 1563 | | portray_global_sets :- |
| 1564 | | (enumerated_sets_precompiled -> Comp=true ; Comp=false), |
| 1565 | | format('ENUMERATED SETS (precompiled=~w):~n ',[Comp]), |
| 1566 | | enumerated_set(GS), |
| 1567 | | format(' ~w ',[GS]), |
| 1568 | | fail. |
| 1569 | | portray_global_sets :- |
| 1570 | | (deferred_sets_precompiled -> Comp=true ; Comp=false), |
| 1571 | | format('~nDEFERRED SETS with some constants (precompiled=~w):~n ',[Comp]), |
| 1572 | | b_partially_deferred_set(GS), |
| 1573 | | (is_b_global_constant(GS,_Nr,Cst) -> true), |
| 1574 | | format(' ~w {~w,...} ',[GS, Cst]), |
| 1575 | | (fixed_deferred_set_size(GS,RCard) |
| 1576 | | -> format('(fixed card ~w) ',[RCard]) |
| 1577 | | ; inferred_maximum_global_set_cardinality(GS,MaxCard) |
| 1578 | | -> format('(max card ~w) ',[MaxCard])), |
| 1579 | | fail. |
| 1580 | | portray_global_sets :- |
| 1581 | | (deferred_sets_precompiled -> Comp=true ; Comp=false), |
| 1582 | | format('~nDEFERRED SETS (precompiled=~w):~n ',[Comp]), |
| 1583 | | b_global_deferred_set(GS), |
| 1584 | | format(' ~w ',[GS]), |
| 1585 | | (fixed_deferred_set_size(GS,RCard) |
| 1586 | | -> format('(fixed card ~w) ',[RCard]) |
| 1587 | | ; inferred_maximum_global_set_cardinality(GS,MaxCard) |
| 1588 | | -> format('(max card ~w) ',[MaxCard])), |
| 1589 | | fail. |
| 1590 | | portray_global_sets :- nl. |