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