1 | | % (c) 2009-2019 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, |
2 | | % Heinrich Heine Universitaet Duesseldorf |
3 | | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) |
4 | | |
5 | | :- module(b_global_sets, |
6 | | [b_get_global_constants/1, b_get_global_enumerated_sets/1, b_get_global_sets/1, |
7 | | lookup_global_constant/2, |
8 | | is_b_global_constant/3, |
9 | | is_used_b_global_constant/3, is_unused_b_global_constant/3, |
10 | | |
11 | | b_global_set/1, b_non_empty_global_set/1, b_empty_global_set/1, |
12 | | b_global_deferred_set/1, |
13 | | b_global_set_with_potential_symmetry/1, b_global_deferred_set_with_card_gt1/1, |
14 | | b_partially_deferred_set/1, |
15 | | |
16 | | unfixed_deferred_set/1, |
17 | | contains_unfixed_deferred_set/1, |
18 | | b_supplementary_global_set/1, % introduced when untyped ids are allowed |
19 | | |
20 | | inferred_minimum_global_set_cardinality/2, |
21 | | inferred_maximum_global_set_cardinality/2, |
22 | | b_exists_global_set_with_potential_symmetry/0, |
23 | | b_global_set_cardinality/2, |
24 | | |
25 | | b_clear_global_set_type_definitions/0, |
26 | | b_check_and_precompile_global_set_type_definitions/0, |
27 | | %b_precompile_used_global_set_information/0, |
28 | | |
29 | | b_get_prob_deferred_set_elements/2,add_prob_deferred_set_elements_to_store/3, |
30 | | prob_deferred_set_element/4, |
31 | | |
32 | | find_inequal_global_set_identifiers/4, |
33 | | static_symmetry_reduction_for_global_sets/1, |
34 | | |
35 | | b_integer_set/1, b_integer_or_string_set/1, |
36 | | b_type2_set/2, try_b_type2global_set/2, |
37 | | |
38 | | %b_fd_type/3, |
39 | | b_get_fd_type_bounds/3, |
40 | | b_fd_card/2, |
41 | | is_global_set_of_cardinality_one/2, %is_global_set_of_cardinality_two/3, |
42 | | global_type/2, global_type_wf/3, |
43 | | get_global_type_value/3, |
44 | | enum_global_type/2, enumerate_global_type_with_enum_warning/3, |
45 | | |
46 | | all_elements_of_type/2, all_elements_of_type_rand/2, |
47 | | |
48 | | set_user_defined_scope/2, |
49 | | |
50 | | fixed_deferred_set_size/2, |
51 | | |
52 | | add_new_global_set/1, |
53 | | generate_fresh_supplementary_global_set/1 |
54 | | %,add_global_set_with_named_constants/2 /* for use by Z,... */ |
55 | | ]). |
56 | | |
57 | | :- use_module(debug). |
58 | | :- use_module(tools). |
59 | | :- use_module(library(lists)). |
60 | | :- use_module(library(ordsets)). |
61 | | :- use_module(self_check). |
62 | | :- use_module(preferences). |
63 | | :- use_module(error_manager). |
64 | | :- use_module(bsyntaxtree). |
65 | | :- use_module(library(between),[between/3]). |
66 | | :- use_module(gensym,[gensym/2]). |
67 | | |
68 | | :- use_module(module_information,[module_info/2]). |
69 | | :- module_info(group,kernel). |
70 | | :- module_info(description,'This module provides support for deferred/enumerated set elements in B.'). |
71 | | |
72 | | :- use_module(bmachine). |
73 | | |
74 | | /* what we call global sets here usually called "given sets", which can be |
75 | | either enumerated or deferred */ |
76 | | |
77 | | |
78 | | :- dynamic b_global_constant/3. % named element of a deferred or enumerated set |
79 | | % for enumerated sets S = {a,b,c} we would have entries a,b,c as b_global_constant |
80 | | % for deferred set S, we may add CONSTANTS which are element of S as b_global_constant |
81 | | :- dynamic unused_b_global_constant/3. % TO DO: compute properly again !! |
82 | | |
83 | | :- dynamic inferred_minimum_global_set_cardinality/2. |
84 | | :- dynamic inferred_maximum_global_set_cardinality/2. |
85 | | |
86 | | b_get_global_enumerated_sets(GSets) :- |
87 | | findall(GS, (b_global_set(GS), is_b_global_constant(GS,_Nr,_Cst)), GSets). |
88 | | b_get_global_sets(GSets) :- |
89 | | findall(GS, b_global_set(GS), GSets). |
90 | | |
91 | | b_get_global_constants(Csts) :- findall(Cst,is_b_global_constant(_,_,Cst),Csts). |
92 | | |
93 | ? | is_b_global_constant(Set,Nr,Cst) :- b_global_constant(Set,Nr,Cst). |
94 | | is_unused_b_global_constant(Set,Nr,Cst) :- unused_b_global_constant(Set,Nr,Cst). |
95 | | is_used_b_global_constant(Set,Nr,Cst) :- |
96 | | b_global_constant(Set,Nr,Cst), \+ unused_b_global_constant(Set,Nr,_). |
97 | | |
98 | | :- dynamic lookup_global_constant/2. |
99 | | %lookup_global_constant(Id,Val) :- |
100 | | % b_global_constant(Set,Nr,Id), % not an indexed lookup ; now we assert lookup_global_constant below |
101 | | % Val = fd(Nr,Set). |
102 | | |
103 | | |
104 | | :- dynamic b_precompiled_global_set/1. |
105 | | b_precompiled_global_set(_) :- print_error('*** b_global_set not precompiled'),fail. |
106 | | |
107 | ? | b_global_set(GS) :- b_precompiled_global_set(GS) ; b_supplementary_global_set(GS). |
108 | | |
109 | | % a version of b_global_set which does not leave a trailing choice point for b_supplementary_global_set around |
110 | | % has to be called with GS ground |
111 | | %b_global_set_ground(GS) :- if(b_precompiled_global_set(GS),true,b_supplementary_global_set(GS)). |
112 | | |
113 | | |
114 | | b_global_deferred_set(GS) :- |
115 | ? | b_global_set(GS), |
116 | ? | \+ is_b_global_constant(GS,_Nr,_Cst). % note : some deferred sets are translated into partially_deferred_set |
117 | | |
118 | | % either an enumerated set with unused constants or a deferred sets where some constants were lifted into the deferred set |
119 | | b_partially_deferred_set(GS) :- |
120 | | b_global_set(GS), |
121 | | (is_b_global_constant(GS,_Nr,_Cst) -> true), % GS is in principle enumerated |
122 | | (unused_b_global_constant(GS,_X,_) -> true). |
123 | | |
124 | | |
125 | | b_global_set_with_potential_symmetry(GS) :- |
126 | ? | b_global_set(GS), |
127 | ? | (b_global_deferred_set_with_card_gt1(GS) -> true |
128 | ? | ; ((unused_b_global_constant(GS,X,_), |
129 | ? | unused_b_global_constant(GS,Y,_), X\=Y) -> true % At least two unused constants exist |
130 | | ; fail)). |
131 | | |
132 | | b_global_deferred_set_with_card_gt1(GS) :- |
133 | ? | b_global_deferred_set(GS), |
134 | | extract_setsize_from_machine_cache(GS,Low,Up), |
135 | | Up>Low. |
136 | | |
137 | | :- volatile b_exists_global_set_with_potential_symmetry/0. |
138 | | :- dynamic b_exists_global_set_with_potential_symmetry/0. |
139 | | |
140 | | b_check_and_precompile_global_set_type_definitions :- |
141 | | /* must be called before the animator can run !! */ |
142 | | b_reset_global_set_type_definitions, |
143 | | retractall(b_exists_global_set_with_potential_symmetry), |
144 | | debug_println(9,'Checking existence of global type definitions: '), |
145 | | debug_print(9,'% '), |
146 | ? | (b_get_named_machine_set(Set,_) % treat enumerated sets first; their cardinality is obvious |
147 | ? | ; b_get_machine_set(Set), \+ b_get_named_machine_set(Set,_) |
148 | | ), |
149 | | add_new_global_set(Set), |
150 | ? | (b_extract_fd_type(Set,LowBnd,UpBnd) /* also computes & caches the size of the SET */ |
151 | | -> debug_print(9,' '),debug_print(9,Set), |
152 | | debug_print(9,'=='),debug_print(9,LowBnd),debug_print(9,'..'),debug_print(9,UpBnd) |
153 | | ; add_internal_error('No b_extract_fd_type/3 solution for global set: ',b_extract_fd_type(Set,_,_)) |
154 | | ),debug_print(9,' '), |
155 | | fail. |
156 | | b_check_and_precompile_global_set_type_definitions :- |
157 | | find_all_used_enumerated_elements(UsedEnumeratedElements), |
158 | ? | b_extract_fd_type(GS,Low,Up), |
159 | ? | (is_b_global_constant(GS,_Nr,_Cst) -> true), % GS is in principle enumerated |
160 | | (user_forced_symmetry(GS) |
161 | | -> ForceSymm=true, print('FORCING SYMMETRY: '), print(GS), print(' ') |
162 | | ; ForceSymm=false |
163 | | ), |
164 | ? | enum_fd(X,Low,Up), /* enumerate all elements of GS */ |
165 | | can_symmetry_reduction_be_applied_to_constant(ForceSymm,GS,X,UsedEnumeratedElements,Display), |
166 | | assert(unused_b_global_constant(GS,X,deferred)), % by storing that it is not used, we enable symmetry reduction |
167 | | debug_println(9,unused_b_global_constant(Display)), |
168 | | fail. |
169 | | b_check_and_precompile_global_set_type_definitions :- |
170 | ? | (b_global_set_with_potential_symmetry(_) |
171 | | -> assert(b_exists_global_set_with_potential_symmetry), |
172 | | debug_println(9,'% Symmetry is potentially useful for this machine') |
173 | | ; true), |
174 | | debug_nl(9), |
175 | | /* clean up: */ |
176 | | reset_global_set_user_defined_scope. |
177 | | |
178 | | % -------------------- |
179 | | |
180 | | % peform MACE style static symmetry reduction for those global constants |
181 | | % that have not already been fixed |
182 | | % 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 |
183 | | |
184 | | static_symmetry_reduction_for_global_sets(_ConstantsState) :- |
185 | | get_preference(try_kodkod_on_load,true),!. % no idea which numbering Kodkod might return/expect ?! |
186 | | static_symmetry_reduction_for_global_sets(_ConstantsState) :- |
187 | | get_preference(use_static_symmetry_detection,false),!. |
188 | | static_symmetry_reduction_for_global_sets(ConstantsState) :- |
189 | | findall(gs_info(GS,FirstAvailableNewIdx,Low,Up,Other), |
190 | | static_symmetry_reduction_possible(GS,FirstAvailableNewIdx,Low,Up,Other),L), |
191 | | maplist(perform_static_symmetry_reduction(ConstantsState),L). |
192 | | |
193 | | :- use_module(static_symmetry_reduction,[static_symmetry_reduction_possible/5, perform_ssr/8]). |
194 | | perform_static_symmetry_reduction(ConstantsState,gs_info(GS,First,Low,Up,Other)) :- |
195 | | if(perform_ssr(Other,[],First,First,GS,Low,Up,ConstantsState),true, |
196 | | add_internal_error('Call failed: ',perform_ssr(Other,[],First,First,GS,Low,Up,ConstantsState))). |
197 | | |
198 | | |
199 | | |
200 | | % -------------------- |
201 | | |
202 | | can_symmetry_reduction_be_applied_to_constant(true,GS,X,_Enums,GS/X). % true means symmetry forced |
203 | | % if symmetry enforced, all elements are subject to symmetry reduction |
204 | | can_symmetry_reduction_be_applied_to_constant(false,GS,X,UsedEnums,Display) :- |
205 | | ( is_b_global_constant(GS,X,Name) -> % sym-reduction can be applied to an enumerated set member |
206 | | nonmember(Name,UsedEnums), % if it's not used anywhere |
207 | | Display=Name |
208 | | ; otherwise -> Display=GS/X). % sym-reduction can be applied to deferred set elements |
209 | | |
210 | | |
211 | | find_all_used_enumerated_elements(Elements) :- |
212 | | b_get_all_used_identifiers(Identifiers), |
213 | | b_get_machine_variables(TVariables),get_texpr_ids(TVariables,Variables), |
214 | | % We assume that b_get_all_used_identifiers/1 is a proper ordset. (sorted with sort/2) |
215 | | % As an optimisation, we remove the variables, they cannot be enumerated elements. |
216 | | list_to_ord_set(Variables,OVariables), |
217 | | ord_subtract(Identifiers,OVariables,Elements),!. |
218 | | find_all_used_enumerated_elements(_Elements) :- |
219 | | add_error_and_fail(b_global_sets, 'find_all_used_enumerated_elements failed'). |
220 | | |
221 | | |
222 | | reset_global_set_user_defined_scope :- |
223 | | retract(global_set_user_defined_scope(GS,_Scope)), |
224 | | (is_b_precompiled_globalset(GS) |
225 | | -> true |
226 | | ; add_error(b_global_sets,'Trying to set scope of unknown SET: ',GS) |
227 | | ),fail. |
228 | | reset_global_set_user_defined_scope. |
229 | | |
230 | | % Return those constants which are implicitly introduced by ProB, but not really |
231 | | % part of the model. These are the elements of a deferred set. |
232 | | b_get_prob_deferred_set_elements(TIds,AllOrVisible) :- |
233 | | findall( TId, |
234 | | ( prob_deferred_set_element(GS,_Nr,Id,AllOrVisible), |
235 | | create_texpr(identifier(Id),global(GS),[],TId)), |
236 | | TIds). |
237 | | |
238 | | add_prob_deferred_set_elements_to_store(OldStore,NewStore,AllOrVisible) :- % add prob_ids |
239 | | %print(add_prob_deferred_set_elements_to_store(OldStore)),nl, |
240 | | findall(bind(Id,fd(Nr,GS)),(prob_deferred_set_element(GS,Nr,Id,AllOrVisible), |
241 | | \+ member(bind(Id,_),OldStore)), |
242 | | NewStore, OldStore). |
243 | | |
244 | | prob_deferred_set_element(GlobalSet,Elem,Id,AllOrVisible) :- |
245 | | b_global_set(GlobalSet), |
246 | | atom_codes(GlobalSet,GlobalSetCodes),append(GlobalSetCodes,ECodes,NameCodes), |
247 | | b_get_fd_type_bounds(GlobalSet,Low,Up), |
248 | | between(Low,Up,Elem), |
249 | | (AllOrVisible = all -> true |
250 | | ; % only visible ones |
251 | | \+ is_b_global_constant(GlobalSet,Elem,_) % the identifier is not used somewhere else |
252 | | ), |
253 | | number_codes(Elem,ECodes), |
254 | | atom_codes(Id,NameCodes), |
255 | | \+ b_global_set(Id), % not used as another SET name |
256 | | \+ lookup_global_constant(Id,_). % not used as another SET element |
257 | | |
258 | | |
259 | | user_forced_symmetry(GS) :- % check if the user defined FORCE_SYMMETRY_GS == TRUE |
260 | | string_concatenate('FORCE_SYMMETRY_',GS,DefName), |
261 | | b_get_typed_definition(DefName,[],F), get_texpr_expr(F,boolean_true). |
262 | | |
263 | | % TO DO: re-add this feature, so that unused enumerated set elements can be detected |
264 | | % :- use_module(bmachine,[used_identifier/1]). |
265 | | %b_precompile_used_global_set_information :- |
266 | | % b_precompute_used_identifiers, |
267 | | % b_global_constant(GlobalSetName,Nr,CstName), |
268 | | % (used_identifier(CstName) -> true |
269 | | % ; (assert(unused_b_global_constant(GlobalSetName,Nr,CstName)), |
270 | | % debug_print(9,unused_b_global_constant(GlobalSetName,Nr,CstName)), debug_nl(9)) |
271 | | % ), |
272 | | % fail. |
273 | | %b_precompile_used_global_set_information :- |
274 | | % retractall(b_exists_deferred_set_or_unused_csts_with_card_gt_1), |
275 | | % ((b_global_deferred_set_with_card_gt1(_GS) ; |
276 | | % (is_unused_b_global_constant(GS,Nr,_), |
277 | | % is_unused_b_global_constant(GS,Nr2,_), Nr2 \= Nr)) |
278 | | % -> (assert(b_exists_deferred_set_or_unused_csts_with_card_gt_1), |
279 | | % print_message('symmetry can be used for this machine')) |
280 | | % ; true |
281 | | % ). |
282 | | |
283 | | %is_global_set_of_cardinality_two(Type,LowBnd,UpBnd) :- |
284 | | % b_get_fd_type_bounds(Type,LowBnd,UpBnd), |
285 | | % LowBnd+1 =:= UpBnd. |
286 | | |
287 | | :- volatile is_global_set_of_cardinality_one/2. |
288 | | :- dynamic is_global_set_of_cardinality_one/2. |
289 | | % should be called when unloading a machine, before type-checking etc... |
290 | | % TO DO: use :- use_module(eventhandling,[register_event_listener/3]). |
291 | | b_clear_global_set_type_definitions :- % nl,print(clearing_gs),nl, |
292 | | retractall(b_supplementary_global_set(_)), |
293 | | b_reset_global_set_type_definitions. |
294 | | |
295 | | b_reset_global_set_type_definitions :- %nl,print(reset_gs),nl, |
296 | | % print_message('resetting global sets'), |
297 | | retractall(b_precompiled_global_set(_)), |
298 | | retractall(enumerated_set(_)), |
299 | | retractall(fixed_deferred_set_size(_,_)), |
300 | | retractall(extract_setsize_from_machine_cache(_,_,_)), |
301 | | retractall(find_minimum_cardinality_cache(_,_)), |
302 | | retractall(is_global_set_of_cardinality_one(_,_)), |
303 | | retractall(b_global_constant(_,_,_)), |
304 | | retractall(lookup_global_constant(_,_)), |
305 | | retractall(unused_b_global_constant(_,_,_)), |
306 | | retractall(inferred_minimum_global_set_cardinality(_,_)), |
307 | | retractall(inferred_maximum_global_set_cardinality(_,_)). |
308 | | |
309 | | add_new_global_set(Set) :- |
310 | | (b_precompiled_global_set(Set) |
311 | | -> add_internal_error('Global set already exists: ',add_new_global_set(Set)) |
312 | | ; assert(b_precompiled_global_set(Set))). |
313 | | |
314 | | :- dynamic b_supplementary_global_set/1. |
315 | | |
316 | | % these should be called before we precompile the global set definitions |
317 | | generate_fresh_supplementary_global_set(FRESHID) :- |
318 | | gensym('__DEFERREDSET__',FRESHID), |
319 | | debug_println(10,generate_fresh_supplementary_global_set(FRESHID)), |
320 | | assert(b_supplementary_global_set(FRESHID)). |
321 | | |
322 | | /* --------------------------------------------------------- */ |
323 | | /* Extracting Finite Domain type information from the B machine */ |
324 | | /* --------------------------------------------------------- */ |
325 | | |
326 | | |
327 | | |
328 | | /* below treats annotations in the form: |
329 | | DEFINITIONS |
330 | | scope_Name == 1..3; |
331 | | scope_Code == 4..8 |
332 | | which inform us about which finidte domain ranges we should |
333 | | give to global sets defined in SETS |
334 | | */ |
335 | | |
336 | | :- use_module(tools_printing,[print_error/1, format_error/2]). |
337 | | :- dynamic extract_setsize_from_machine_cache/3. |
338 | | extract_setsize_from_machine_cache(_,_,_) :- |
339 | | print_error('*** extract_setsize_from_machine not precompiled'),fail. |
340 | | |
341 | | try_extract_setsize_from_machine(GlobalSetName,_LowBound,_UpBound) :- |
342 | | start_extracting_setsize(GlobalSetName),!,fail. % cyclic dependency; abort computation |
343 | | try_extract_setsize_from_machine(GlobalSetName,LowBound,UpBound) :- |
344 | | extract_setsize_from_machine(GlobalSetName,LowBound,UpBound). |
345 | | |
346 | | extract_setsize_from_machine(GlobalSetName,LowBound,UpBound) :- |
347 | | extract_setsize_from_machine_cache(GlobalSetName,L,U),!, |
348 | | LowBound=L,UpBound=U. |
349 | | extract_setsize_from_machine(GlobalSetName,LowBound,UpBound) :- |
350 | | start_extracting_setsize_from_machine(GlobalSetName), |
351 | | b_get_named_machine_set(GlobalSetName,ListOfConstants), |
352 | | !, |
353 | | assert_enumerated_set(GlobalSetName,ListOfConstants,LowBound,UpBound), % will also assert_extract_setsize_from_machine_cache |
354 | | (get_user_defined_scope(GlobalSetName,DL,DU), |
355 | | DU-DL =\= UpBound-LowBound |
356 | | -> add_error(extract_setsize_from_machine,'Conflict between scope_ Definition and explicit enumeration of SET:',GlobalSetName) |
357 | | ; true |
358 | | ). |
359 | | extract_setsize_from_machine(GlobalSetName,L,U) :- |
360 | | b_supplementary_global_set(GlobalSetName),!, % deferred set that was added for untyped ID,... |
361 | | default_deferred_set_bounds(LowBound,UpBound), |
362 | | assert_extract_setsize_from_machine_cache(GlobalSetName,LowBound,UpBound), |
363 | | (LowBound,UpBound)=(L,U). |
364 | | extract_setsize_from_machine(GlobalSetName,L,U) :- |
365 | | /* we have a DEFERRED SET */ |
366 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,LowBound,UpBound), |
367 | | (LowBound=UpBound -> assert(is_global_set_of_cardinality_one(GlobalSetName,LowBound)) ; true), |
368 | | assert_extract_setsize_from_machine_cache(GlobalSetName,LowBound,UpBound), |
369 | | (b_get_disjoint_constants_of_type(GlobalSetName, DisjointConstants,_) |
370 | | -> /* 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 */ |
371 | | debug_println(9,adding_disjoint_constants(GlobalSetName,DisjointConstants)), |
372 | | ~~mnf(add_named_constants_to_global_set(GlobalSetName,DisjointConstants)) |
373 | | ; true |
374 | | ), |
375 | | debug_println(9,setsize(GlobalSetName,LowBound,UpBound)), |
376 | | (LowBound,UpBound)=(L,U). |
377 | | |
378 | | /* for Deferred SETS : */ |
379 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,L,U) :- |
380 | | b_extract_values_clause_assignment(GlobalSetName,_Type,TVal), |
381 | | (TVal = b(interval(Low,Up),_,_) -> true |
382 | | ; add_error(b_global_sets,'VALUES clause must set deferred set to an interval: ',GlobalSetName,TVal),fail), |
383 | | (evaluable_integer_expression(Low,[],LowBound) -> true |
384 | | ; add_error(b_global_sets,'Cannot extract lower bound from VALUES interval for: ',GlobalSetName,Low),fail), |
385 | | (evaluable_integer_expression(Up,[],UpBound) -> true |
386 | | ; add_error(b_global_sets,'Cannot extract upper bound from VALUES interval for: ',GlobalSetName,Up),fail), |
387 | | !, |
388 | | check_compatible_with_user_scope(GlobalSetName,UpBound,LowBound), |
389 | | LowBound=L,UpBound=U. |
390 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,L,U) :- |
391 | ? | b_get_properties_from_machine(Properties), member_in_conjunction(C,Properties), |
392 | | % check if the PROPERTIES contain an expression of the form card(GS) = Nr |
393 | | is_equality_card_global_set(C,GlobalSetName,Properties,Card),!, |
394 | | assert(fixed_deferred_set_size(GlobalSetName,Card)), |
395 | | LowBound=1, UpBound = Card, |
396 | | check_compatible_with_user_scope(GlobalSetName,UpBound,LowBound), |
397 | | LowBound=L,UpBound=U. |
398 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,L,U) :- /* check if there is a scope_ DEFINITION */ |
399 | | get_user_defined_scope(GlobalSetName,LowBound,UpBound),!, |
400 | | LowBound=L,UpBound=U. |
401 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,LowBound,UpBound) :- |
402 | | get_preference(globalsets_fdrange,DefaultUpperBound), |
403 | | find_minimum_cardinality(GlobalSetName,MinCard), |
404 | | assert(inferred_minimum_global_set_cardinality(GlobalSetName,MinCard)), |
405 | | !, |
406 | | LowBound = 1, |
407 | | (find_maximum_cardinality(GlobalSetName,MaxCard) |
408 | | -> (MaxCard=MinCard -> assert(fixed_deferred_set_size(GlobalSetName,MinCard)), |
409 | | debug_println(9,fixed_deferred_set_size(GlobalSetName,MinCard)), |
410 | | UpBound = MinCard |
411 | | ; DefaultUpperBound>MaxCard -> |
412 | | debug_println(9,reducing_deferred_set_size(GlobalSetName,MaxCard)), |
413 | | UpBound=MaxCard |
414 | | ; DefaultUpperBound<MinCard -> |
415 | | debug_println(9,inferred_minimum_global_set_cardinality(GlobalSetName,MinCard)), |
416 | | UpBound=MinCard |
417 | | ; otherwise -> UpBound is MinCard |
418 | | ) |
419 | | ; MinCard>DefaultUpperBound -> UpBound=MinCard |
420 | | ; UpBound=DefaultUpperBound), |
421 | | debug_println(4,inferred(GlobalSetName,UpBound)). |
422 | | extract_DEFERRED_setsize_from_machine(GlobalSetName,LowBound,UpBound) :- |
423 | | % No Minimum cardinality was inferred |
424 | | LowBound=1, get_preference(globalsets_fdrange,DefaultUpperBound), |
425 | | find_maximum_cardinality(GlobalSetName,MaxCard), |
426 | | !, |
427 | | (MaxCard=1 -> UpBound=1, assert(fixed_deferred_set_size(GlobalSetName,1)) |
428 | | ; DefaultUpperBound>MaxCard -> |
429 | | debug_println(9,reducing_deferred_set_size(GlobalSetName,MaxCard)), |
430 | | UpBound=MaxCard |
431 | | % does not have the case: DefaultUpperBound<MinCard -> UpBound=MinCard |
432 | | ; UpBound=DefaultUpperBound). |
433 | | extract_DEFERRED_setsize_from_machine(_GlobalSetName,LowBound,UpBound) :- |
434 | | % No Minimum cardinality was inferred |
435 | | default_deferred_set_bounds(LowBound,UpBound). |
436 | | |
437 | | default_deferred_set_bounds(1,UpBound) :- get_preference(globalsets_fdrange,UpBound). |
438 | | |
439 | | check_compatible_with_user_scope(GlobalSetName,UpBound,LowBound) :- |
440 | | (get_user_defined_scope(GlobalSetName,DL,DU), |
441 | | DU-DL =\= UpBound-LowBound |
442 | | -> add_error(extract_DEFERRED_setsize_from_machine,'Conflict between scope_ Definition and PROPERTIES:',GlobalSetName) |
443 | | ; true |
444 | | ). |
445 | | |
446 | | |
447 | | find_maximum_cardinality(GlobalSetName,MaxCard) :- |
448 | | find_maximum_cardinality1(GlobalSetName,MaxCard), |
449 | | assert(inferred_maximum_global_set_cardinality(GlobalSetName,MaxCard)). |
450 | | |
451 | | find_maximum_cardinality1(GlobalSetName,MaxCard) :- |
452 | | b_get_machine_constants(Csts), |
453 | | b_get_properties_from_machine(Properties), |
454 | | findall(MC2,find_maximum_cardinality2(Csts,Properties,GlobalSetName,MC2),List), |
455 | | debug_println(9,maxcard_list(GlobalSetName,List)), |
456 | | min_member(MaxCard,List). |
457 | | find_maximum_cardinality2(_Csts,Properties,GlobalSetName,MaxCard) :- |
458 | | get_card_upper_bound(Properties,GlobalSetName,MaxCard). |
459 | | find_maximum_cardinality2(_Csts,Properties,GlobalSetName,MaxCard) :- |
460 | | get_equality(Properties,GlobalSetName,R,RestProp), |
461 | | maximum_cardinality_of_expression(R,RestProp,MaxCard). |
462 | | find_maximum_cardinality2(_Csts,Properties,GlobalSetName,MaxCard) :- |
463 | ? | member_in_conjunction(C,Properties), |
464 | | get_texpr_expr(C,partition(Set,DisjSets)), |
465 | | global_set_identifier(Set,GlobalSetName), |
466 | | max_partition_card(DisjSets,Properties,0,MaxCard). |
467 | | |
468 | | get_equality(Properties,ID,R,RestProp) :- |
469 | ? | select_member_in_conjunction(C,Properties,RestProp), |
470 | | get_texpr_expr(C,equal(LHS,RHS)), |
471 | | ( get_texpr_expr(LHS,identifier(ID)), R=RHS |
472 | | ; get_texpr_expr(RHS,identifier(ID)), R=LHS ). |
473 | | |
474 | | % find card(ID) <= MaxCard |
475 | | get_card_upper_bound(Properties,ID,MaxCard) :- |
476 | | LHS = b(card(TID),_,_), |
477 | | get_texpr_expr(TID,identifier(ID)), |
478 | ? | select_member_in_conjunction(C,Properties,RestProp), |
479 | | ( get_texpr_expr(C,less_equal(LHS,RHS)), Strict=false ; |
480 | | get_texpr_expr(C,greater_equal(RHS,LHS)), Strict=false ; |
481 | | get_texpr_expr(C,less(LHS,RHS)), Strict=true ; |
482 | | get_texpr_expr(C,greater(RHS,LHS)), Strict=true |
483 | | ), |
484 | | evaluable_integer_expression(RHS,RestProp,R), |
485 | | (Strict=true -> MaxCard is R-1 ; MaxCard = R). |
486 | | |
487 | | maximum_cardinality_of_expression(b(E,_,_),Props,MC) :- maximum_cardinality_of_expression2(E,Props,MC). |
488 | | maximum_cardinality_of_expression2(set_extension(SetExtElements),_,MaxCard) :- |
489 | | length(SetExtElements,MaxCard). |
490 | | % we assume that all elements in List can be different |
491 | | maximum_cardinality_of_expression2(union(A,B),Props,MaxCard) :- |
492 | | maximum_cardinality_of_expression(A,Props,MA), |
493 | | maximum_cardinality_of_expression(B,Props,MB), MaxCard is MA+MB. |
494 | | maximum_cardinality_of_expression2(intersection(A,B),Props,MaxCard) :- |
495 | | maximum_cardinality_of_expression(A,Props,MA), |
496 | | maximum_cardinality_of_expression(B,Props,MB), MaxCard is min(MA,MB). |
497 | | maximum_cardinality_of_expression2(set_subtraction(A,_),Props,MaxCard) :- |
498 | | maximum_cardinality_of_expression(A,Props,MaxCard). |
499 | | maximum_cardinality_of_expression2(cartesian_product(A,B),Props,MaxCard) :- |
500 | | maximum_cardinality_of_expression(A,Props,MA), |
501 | | maximum_cardinality_of_expression(B,Props,MB), MaxCard is MA*MB. |
502 | | maximum_cardinality_of_expression2(identifier(ID),Props,MaxCard) :- % tested in testcase 1917 |
503 | | get_equality(Props,ID,RHS,RestProps),!, % remove equality to avoid loops, TO DO: try several equalities? |
504 | | maximum_cardinality_of_expression(RHS,RestProps,MaxCard). |
505 | | maximum_cardinality_of_expression2(empty_set,_,0). |
506 | | % TO DO: generalized union... |
507 | | %maximum_cardinality_of_expression2(Ex,_) :- nl, print(uncov_max_partition_card(Ex)),nl,nl,fail. |
508 | | % TO DO: other cases |
509 | | |
510 | | max_partition_card([],_,Acc,Acc). |
511 | | max_partition_card([TPartSet|T],Properties,Acc,Res) :- |
512 | | maximum_cardinality_of_expression(TPartSet,Properties,Max), |
513 | | NAcc is Acc+Max, |
514 | | max_partition_card(T,Properties,NAcc,Res). |
515 | | |
516 | | |
517 | | :- dynamic find_minimum_cardinality_cache/2. |
518 | | find_minimum_cardinality(GS,MC) :- |
519 | | find_minimum_cardinality_cache(GS,Nr),!, %print(min_card_cache(GS,Nr)),nl, |
520 | | % avoid loops; e.g., if we have N1 >->> N2 and N2 >->> N1 |
521 | | number(Nr), |
522 | | MC=Nr. |
523 | | find_minimum_cardinality(GlobalSetName,Res) :- % print(find_min_card(GlobalSetName)),nl, |
524 | | /* try to find out from the Properties whether there is an implied minimum cardinality */ |
525 | | b_get_machine_constants(Csts), |
526 | | b_get_properties_from_machine(Properties), |
527 | | assert(find_minimum_cardinality_cache(GlobalSetName,unknown)), |
528 | | findall(MinCard2,find_minimum_cardinality2(Csts,Properties,GlobalSetName,MinCard2),List), |
529 | | debug_println(9,mincard_list(GlobalSetName,List)), |
530 | | max_member(MinCard,List), % we do not allow empty global sets anymore, allow_empty_global_sets is obsolete |
531 | | MinCard>0, |
532 | | retract(find_minimum_cardinality_cache(GlobalSetName,_)), |
533 | | assert(find_minimum_cardinality_cache(GlobalSetName,MinCard)), |
534 | | Res=MinCard. |
535 | | |
536 | | find_minimum_cardinality2(_,Properties,GlobalSetName,MinCard) :- |
537 | ? | member_in_conjunction(C,Properties), |
538 | | find_minimum_cardinality3(C,Properties,GlobalSetName,MinCard). |
539 | | find_minimum_cardinality2(_Constants,_Properties,GlobalSetName,MinCard) :- |
540 | | %print(trying_find_min_card(GlobalSetName,Constants)),nl, |
541 | | b_get_disjoint_constants_of_type(GlobalSetName, DisjointConstants,_), |
542 | | length(DisjointConstants,MinCard), MinCard>1. |
543 | | |
544 | | find_minimum_cardinality3(C,Properties,GlobalSetName,MinCard) :- |
545 | | % check if the PROPERTIES contain an expression of the form card(GS) >= Nr or similar |
546 | | is_greaterequalthan_card_global_set(C,GlobalSetName,Properties,MinCard). |
547 | | find_minimum_cardinality3(C,Properties,GlobalSetName,MinCard) :- |
548 | | % find things like partition(GlobalSetName,S1,...,SN) |
549 | | get_texpr_expr(C,partition(Set,DisjSets)), |
550 | | global_set_identifier(Set,GlobalSetName), |
551 | | % print(partition(Set,DisjSets,GlobalSetName)),nl, |
552 | | find_min_card_of_disjoint_sets(DisjSets,Properties,MinCard). % ,print(min(MinCard)),nl. |
553 | | find_minimum_cardinality3(C,_Properties,GlobalSetName,MinCard) :- |
554 | | get_texpr_expr(C,member(_Something,SURJ)), |
555 | ? | is_surjection(SURJ,Set,OTHERSET), |
556 | | global_set_identifier(Set,GlobalSetName), |
557 | | \+ global_set_identifier(OTHERSET,GlobalSetName), % Surjection on itself; no use in narrowing down cardinality |
558 | | (minimum_cardinality(OTHERSET,MinCard) |
559 | | -> true |
560 | | ; print('*** could not compute mincard: '), print(OTHERSET),nl,fail), |
561 | | (MinCard=inf -> add_error(b_global_sets,'Set has to be of infinite cardinality: ',GlobalSetName),fail ; true). |
562 | | |
563 | | % is a surjective function |
564 | | is_surjection(SURJ,FromSet,ToSet) :- get_texpr_expr(SURJ,total_surjection(FromSet,ToSet)). |
565 | | is_surjection(SURJ,FromSet,ToSet) :- get_texpr_expr(SURJ,partial_surjection(FromSet,ToSet)). |
566 | | is_surjection(SURJ,FromSet,ToSet) :- get_texpr_expr(SURJ,total_bijection(FromSet,ToSet)). % here we know the exact cardinality - TO DO: use this fact |
567 | | is_surjection(SURJ,FromSet,ToSet) :- get_texpr_expr(SURJ,partial_bijection(FromSet,ToSet)). |
568 | | is_surjection(SURJ,FromSet,ToSet) :- get_texpr_expr(SURJ,total_injection(ToSet,FromSet)). % inverse is surjective |
569 | | |
570 | | |
571 | | |
572 | | % compute the minmum cardinality of an expression |
573 | | minimum_cardinality(ID,MinCard) :- global_set_identifier(ID,GlobalSet),!, |
574 | | (b_extract_fd_type(GlobalSet,LowBnd,UpBnd) |
575 | | -> MinCard is 1+UpBnd-LowBnd |
576 | | ; print('*** Do not know card of : '), print(GlobalSet),nl, |
577 | | % TO DO: do full blown cardinality analysis of deferred sets |
578 | | fail). |
579 | | minimum_cardinality(b(EXPR,TYPE,_INFO),MinCard) :- |
580 | | minimum_cardinality2(EXPR,TYPE,MinCard). |
581 | | |
582 | | %minimum_cardinality2(A,T,R) :- print(mincard(A,T,R)),nl,fail. |
583 | | minimum_cardinality2(bool_set,_,2). |
584 | | minimum_cardinality2(cartesian_product(A,B),_,Res) :- minimum_cardinality(A,CA), |
585 | | minimum_cardinality(B,CB), kernel_objects:safe_mul(CA,CB,Res). |
586 | | minimum_cardinality2(interval(A,B),_,Res) :- |
587 | | b_get_properties_from_machine(Properties), |
588 | | evaluable_integer_expression(A,Properties,EA), |
589 | | evaluable_integer_expression(B,Properties,EB), |
590 | | (EB >= EA -> Res is EB+1-EA ; Res=0). |
591 | | % TO DO: enumerated sets, ... seq, relations, ... ? |
592 | | |
593 | | |
594 | | find_min_card_of_disjoint_sets([],_Properties,0). |
595 | | find_min_card_of_disjoint_sets([H|T],Properties,R) :- |
596 | | find_min_card_of_disjoint_sets(T,Properties,TN), |
597 | | (minimum_cardinality_of_set(H,Properties,MC) |
598 | | -> true %,print(min_card(MC,H)),nl |
599 | | ; add_internal_error('Call failed: ',minimum_cardinality_of_set(H,Properties,MC)), MC=1), |
600 | | R is TN+MC. |
601 | | |
602 | | |
603 | | minimum_cardinality_of_set(b(Expr,set(Type),_),Properties,MC) :- |
604 | | minimum_cardinality_of_set_aux(Expr,Type,Properties,MC). |
605 | | |
606 | | %minimum_cardinality_of_set_aux(P,Type,_,_MC) :- print(min_card(P,Type)),nl,fail. |
607 | | minimum_cardinality_of_set_aux(set_extension(List),GlobalSetName,Properties,MinCard) :- !, |
608 | | def_get_texpr_ids(List,AtomicIDList), |
609 | | find_inequal_global_set_identifiers(AtomicIDList,GlobalSetName,Properties,DisjointConstants), |
610 | | length(DisjointConstants,MinCard). % , print(disj_set_ext(MinCard,DisjointConstants)),nl,nl. |
611 | | minimum_cardinality_of_set_aux(identifier(CST),_Type,_Properties,MinCard) :- |
612 | | find_minimum_cardinality_cache(CST,Nr),!, |
613 | | number(Nr), |
614 | | MinCard=Nr. |
615 | | minimum_cardinality_of_set_aux(identifier(CST),Type,Properties,MinCard) :- |
616 | | Cst_Template = b(identifier(CST),set(Type),_), |
617 | | min_card_of_identifier(Cst_Template,Type,Properties,MC), |
618 | | !, |
619 | | MinCard=MC, |
620 | | assert(find_minimum_cardinality_cache(CST,MC)). % global sets and constants cannot have the same name |
621 | | % TO DO: add more cases ? sequence extension cannot appear |
622 | | minimum_cardinality_of_set_aux(_,_,_,0). |
623 | | |
624 | | % determine minimum cardinality of an identifier; usually a constant (cannot be another global set due to B typing). |
625 | | min_card_of_identifier(Cst_Template,_Type,Properties,MinCard) :- |
626 | | member_in_conjunction(EqCardExpr,Properties), % look for card(CstTemplate) = MinCard |
627 | | is_equality_card_expression(EqCardExpr,Properties,Cst_Template,MinCard),!. % this is actually an exact card |
628 | | min_card_of_identifier(Cst_Template,_Type,Properties,MC) :- |
629 | | get_texpr_expr(PT,partition(Cst_Template,DisjSets)), |
630 | | member_in_conjunction(PT,Properties), |
631 | | find_min_card_of_disjoint_sets(DisjSets,Properties,MC),!. |
632 | | min_card_of_identifier(Cst_Template,Type,Properties,MinCard) :- % x:ID => card(ID) >= 1 |
633 | | get_texpr_id(TID,ID), |
634 | | get_texpr_expr(EL,member(TID,Cst_Template)), |
635 | | findall(ID,member_in_conjunction(EL,Properties),AtomicIDList), |
636 | | % find all identifiers such that we have id : CST |
637 | | AtomicIDList \= [], |
638 | | !, |
639 | | find_inequal_global_set_identifiers(AtomicIDList,Type,Properties,DisjointConstants), |
640 | | length(DisjointConstants,MinCard). |
641 | | min_card_of_identifier(Cst_Template,_Type,Properties,MC) :- % x:ID => card(ID) >= 1 |
642 | | get_texpr_expr(EL,member(_,Cst_Template)), |
643 | | member_in_conjunction(EL,Properties), |
644 | | %translate:print_bexpr(EL),nl, |
645 | | !, |
646 | | MC=1. |
647 | | min_card_of_identifier(Cst_Template,Type,Properties,MC) :- % ID /= {} => card(ID) >= 1 |
648 | | EMPTYSET = b(empty_set,set(Type),_), |
649 | | get_texpr_expr(NEQ,not_equal(LHS,RHS)), |
650 | | member_in_conjunction(NEQ,Properties), |
651 | | (LHS=Cst_Template, RHS=EMPTYSET ; |
652 | | RHS=Cst_Template, LHS=EMPTYSET ), |
653 | | %print(neq(NEQ,CST,RHS)),nl, |
654 | | !, |
655 | | MC=1. |
656 | | |
657 | | |
658 | | % the following could be further improved |
659 | | % try and find out how many different identifiers are in a list in the context of a predicate Prop |
660 | | % it is also called by b_get_disjoint_constants_of_type_calc in bmachine |
661 | | %find_inequal_global_set_identifiers(L,GS,_P,_R) :- print(find_inequal_global_set_identifiers(L,GS)),nl,fail. |
662 | | |
663 | | :- use_module(bsyntaxtree,[conjunction_to_list/2]). |
664 | | find_inequal_global_set_identifiers(IDS,GS,Prop,Res) :- |
665 | | conjunction_to_list(Prop,Preds), |
666 | | % first apply nested partition predicates as much as possible, to obtain maximally large partitions of distinct elements |
667 | | get_relevant_partition_preds(Preds,GS,RelevantPartitionPreds), % TO DO: cache this computation |
668 | | findall(TP,find_a_transitive_partition(RelevantPartitionPreds,TP),TransitivePartitions), |
669 | | append(TransitivePartitions,Preds,Preds2), |
670 | | find_inequal_global_set_identifiers2(IDS,GS,Preds2,Res). |
671 | | |
672 | | find_inequal_global_set_identifiers2([],_,_,[]). |
673 | | find_inequal_global_set_identifiers2([ID|T],GS,Preds,Res) :- |
674 | | (atomic(ID) -> true |
675 | | ; add_internal_error('Not atomic id: ',find_inequal_global_set_identifiers([ID|T],GS,Preds,Res)),fail), |
676 | | findall(ID2, (member(C,Preds), %print(chck(C)),nl, |
677 | | inequality(C,GS,ID,ID2,Preds), |
678 | | member(ID2,T)), DiffIDs), |
679 | | sort(DiffIDs,SortedDiffIDs), % also remove duplicates |
680 | | (SortedDiffIDs=[],T=[_,_|_] % then try to proceed with T, ignoring ID |
681 | | -> find_inequal_global_set_identifiers2(T,GS,Preds,Res) |
682 | | ; find_inequal_global_set_identifiers2(SortedDiffIDs,GS,Preds,RT), Res = [ID|RT] |
683 | | ). |
684 | | |
685 | | get_relevant_partition_preds(Preds,GS,SortedRelevantPartitionPreds) :- |
686 | | % detect which partition predicates are relevant for this global set |
687 | | findall(partition(Set,Subsets), |
688 | | (member(C,Preds), |
689 | | is_relevant_partition(C,GS,Set,Subsets)), RelevantPartitionPreds), |
690 | | sort(RelevantPartitionPreds,SortedRelevantPartitionPreds). |
691 | | is_relevant_partition(b(partition(Set,Subsets),pred,_),GS,Set,Subsets) :- |
692 | | get_texpr_type(Set,set(GS)). % , translate:print_bexpr(b(partition(Set,Subsets),pred,[])),nl. |
693 | | |
694 | | % if we have partition(A,[B,C]) and partition(B,[E,F]) --> generate partition(A,[E,F,C]) |
695 | | find_a_transitive_partition(Preds,b(partition(A,C),pred,[generated])) :- |
696 | | member(partition(A,B),Preds), |
697 | | inline_subsets(B,Preds,Inlined,C), Inlined==inlined. |
698 | | inline_subsets([],_,_,[]). |
699 | | inline_subsets([Subset1|TS],Preds,inlined,Result) :- get_texpr_id(Subset1,SubID), |
700 | | %prefix(Pred,[partition(S2,SubList)|Pred2]), |
701 | | select(partition(Subset2,SubList),Preds,Pred2), |
702 | | get_texpr_id(Subset2,SubID), |
703 | | format('Inlining nested partition predicate for set ~w~n',[SubID]), |
704 | | !, |
705 | | append(SubList,TS,NewList), |
706 | | inline_subsets(NewList,Pred2,_,Result). |
707 | | inline_subsets([Subset1|TS],Preds,Inlined,[Subset1|TSRes]) :- inline_subsets(TS,Preds,Inlined,TSRes). |
708 | | |
709 | | |
710 | | inequality(b(P,pred,_),GS,ID,OtherID,FullProps) :- |
711 | | inequality_aux(P,GS,ID,OtherID,FullProps). |
712 | | inequality_aux(not_equal(I1,I2),GS,ID,OtherID,_) :- |
713 | | gs_identifier(I1,GS,ID1), gs_identifier(I2,GS,ID2), |
714 | | (ID=ID1 -> OtherID=ID2 ; ID=ID2,OtherID=ID1). |
715 | | inequality_aux(not_equal(I1,I2),GS,ID,OtherID,_) :- % treat {aa} \= {bb} or more generally {aa,bb} \= {aa,cc} |
716 | | % two identifiers of global sets need not be different |
717 | | % in order for two set extensions to be inequal |
718 | | % first we check if I1, I2 are in fact subsets of global sets |
719 | | get_texpr_type(I1,set(global(_))), |
720 | | get_texpr_expr(I1,set_extension(IDs1)), % TODO: is there something more general than a set_extension? |
721 | | get_texpr_expr(I2,set_extension(IDs2)), % should full sets or set identifiers added? |
722 | | % remove the common elements and see if we find a pair of identifiers |
723 | | maplist(remove_all_infos_and_ground,IDs1,IDs1NoInfos), maplist(remove_all_infos_and_ground,IDs2,IDs2NoInfos), |
724 | | % TODO: maybe it is same to assume the order? |
725 | | list_to_ord_set(IDs1NoInfos,IDs1Set), list_to_ord_set(IDs2NoInfos,IDs2Set), |
726 | | ord_subtract(IDs1Set,IDs2Set,[E1]), % each set has one identifier that is not in the other one |
727 | | ord_subtract(IDs2Set,IDs1Set,[E2]), |
728 | | E1 \= E2, % and they are not the same |
729 | | gs_identifier(E1,GS,ID1), gs_identifier(E2,GS,ID2), |
730 | | (ID=ID1 -> OtherID=ID2 ; ID=ID2,OtherID=ID1). |
731 | | inequality_aux(negation( b(equal(I1,I2),pred,_)),GS,ID,OtherID,_) :- |
732 | | gs_identifier(I1,GS,ID1), gs_identifier(I2,GS,ID2), |
733 | | (ID=ID1 -> OtherID=ID2 ; ID=ID2,OtherID=ID1). |
734 | | inequality_aux(partition(Set,DisjSets),GS,ID,OtherID,Preds) :- |
735 | | get_texpr_type(Set,set(GS)), % only look at relevant partitions of correct type |
736 | | %nl,print(part(_Set,DisjSets,GS,ID,OtherID)),nl, |
737 | | % detect such things as partition(_,{...ID...},...{...OtherID...}...) |
738 | | select(Set1,DisjSets,Rest), set_extension_list(Set1,Preds,List1), |
739 | | member(O1,List1), gs_identifier(O1,GS,ID), |
740 | | member(Set2,Rest), set_extension_list(Set2,Preds,List2), |
741 | | member(O2,List2), gs_identifier(O2,GS,OtherID). |
742 | | inequality_aux(AllDiff,GS,ID,OtherID,Preds) :- |
743 | | all_different(AllDiff,List1,GS,Preds), |
744 | | select(O1,List1,RestList), gs_identifier(O1,GS,ID), |
745 | | member(O2,RestList), gs_identifier(O2,GS,OtherID). |
746 | | |
747 | | % detect card({x1,....,xn}) = n as an all-different predicate |
748 | | all_different(equal(A,B),List,Type,Preds) :- |
749 | | all_diff_aux(A,B,List,Type,Preds) ; all_diff_aux(B,A,List,Type,Preds). |
750 | | all_diff_aux(A,B,List1,Type,Preds) :- |
751 | | A=b(card(Set1),_,_), |
752 | | get_texpr_type(Set1,set(Type)), % TO DO: also match seq type ? (in case we use predicate in other context) |
753 | | set_extension_list(Set1,Preds,List1), |
754 | | evaluable_integer_expression(B,b(truth,pred,[]),Card), |
755 | | length(List1,Card). |
756 | | |
757 | | set_extension_list(b(set_extension(List),_,_),_,List). |
758 | | set_extension_list(b(identifier(ID),_,_),Preds,List) :- |
759 | | % accept card(GS) = nr & ... GS = {....} |
760 | | member(Eq,Preds), |
761 | | equality(Eq,TID,b(set_extension(List),_,_)), |
762 | | get_texpr_id(TID,ID). |
763 | | |
764 | | equality(b(equal(LHS,RHS),pred,_),L,R) :- ( (L,R)=(LHS,RHS) ; (L,R)=(RHS,LHS) ). |
765 | | |
766 | | gs_identifier(b(identifier(ID),GS,_),GS,ID). |
767 | | |
768 | | :- 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) |
769 | | ). |
770 | | :- 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), |
771 | | GS=='TITLE', Card=4) |
772 | | ). |
773 | | |
774 | | is_equality_card_expression(TE,Properties,Expr,Card) :- |
775 | ? | get_texpr_expr(TE,equal(LHS,RHS)), |
776 | ? | get_texpr_expr(LHS,L), get_texpr_expr(RHS,R), |
777 | ? | ( L=card(Expr),evaluable_integer_expression(R,Properties,Card) |
778 | | ; R=card(Expr),evaluable_integer_expression(L,Properties,Card)). |
779 | | |
780 | | is_equality_card_global_set(TE,GlobalSet,Properties,Card) :- |
781 | ? | is_equality_card_expression(TE,Properties,Expr,Card), |
782 | | global_set_identifier(Expr,GlobalSet), |
783 | | get_texpr_type(Expr,set(global(GlobalSet))). |
784 | | |
785 | | %evaluable_integer_expression(X,Y) :- print(ev(X,Y)),nl,fail. |
786 | | % TO DO: maybe we should do some kind of constant folding or call b_compile ?? |
787 | | evaluable_integer_expression(b(E,integer,_),Properties,R) :- |
788 | | evaluable_integer_expression(E,Properties,R). |
789 | | evaluable_integer_expression(max_int,_,R) :- preferences:preference(maxint,R). |
790 | | evaluable_integer_expression(min_int,_,R) :- preferences:preference(minint,R). |
791 | | evaluable_integer_expression(identifier(ID),Properties,R) :- |
792 | ? | get_texpr_expr(Eq,equal(LHS,RHS)), get_texpr_id(LHS,ID), |
793 | ? | select_member_in_conjunction(Eq,Properties,RestProp), % avoids loops; but an identifier cannot be used multiple times N+N |
794 | | evaluable_integer_expression(RHS,RestProp,R). |
795 | | evaluable_integer_expression(card(Set),_Properties,Card) :- % only works if we have already treated GlobalSetName ! TO DO: infer CLPFD constraints or treat sets in some order or look for definition in Properties (beware of cycles) |
796 | | Set = b(identifier(GlobalSetName),set(global(GlobalSetName)),_), |
797 | | extract_setsize_from_machine_cache(GlobalSetName,L,U), |
798 | | Card is U+1-L. |
799 | | evaluable_integer_expression(integer(Card),_,Card). |
800 | | evaluable_integer_expression(div(A,B),Properties,Res) :- |
801 | | evaluable_integer_expression(A,Properties,RA), |
802 | | evaluable_integer_expression(B,Properties,RB), Res is RA // RB. |
803 | | evaluable_integer_expression(multiplication(A,B),Properties,Res) :- |
804 | | evaluable_integer_expression(A,Properties,RA), |
805 | | evaluable_integer_expression(B,Properties,RB), Res is RA*RB. |
806 | | evaluable_integer_expression(add(A,B),Properties,Res) :- |
807 | | evaluable_integer_expression(A,Properties,RA), |
808 | | evaluable_integer_expression(B,Properties,RB), Res is RA+RB. |
809 | | evaluable_integer_expression(minus(A,B),Properties,Res) :- |
810 | | evaluable_integer_expression(A,Properties,RA), |
811 | | evaluable_integer_expression(B,Properties,RB), Res is RA-RB. |
812 | | evaluable_integer_expression(unary_minus(A),Properties,Res) :- |
813 | | evaluable_integer_expression(A,Properties,RA), Res is 0-RA. |
814 | | |
815 | | global_set_identifier(C,GS) :- get_texpr_expr(C,BE), global_set_identifier2(BE,GS). |
816 | | global_set_identifier2(identifier(GlobalSet),GlobalSet). |
817 | | global_set_identifier2(value(global_set(GlobalSet)),GlobalSet). % generated by Z |
818 | | |
819 | | % check for card(GlobalSet) >= Card or similar |
820 | | is_greaterequalthan_card_global_set(TE,GlobalSet,Properties,Card) :- |
821 | | (get_texpr_expr(TE,greater_equal(LHS,RHS)) ; |
822 | | get_texpr_expr(TE,less_equal(RHS,LHS)) ), |
823 | | get_texpr_expr(LHS,card(C)), |
824 | | get_minimum_or_exact_value(RHS,Properties,Card), |
825 | | global_set_identifier(C,GlobalSet), |
826 | | get_texpr_type(C,set(global(GlobalSet))). |
827 | | is_greaterequalthan_card_global_set(TE,GlobalSet,Properties,Card1) :- |
828 | ? | (get_texpr_expr(TE,greater(LHS,RHS)) ; |
829 | | get_texpr_expr(TE,less(RHS,LHS)) ), |
830 | | get_texpr_expr(LHS,card(C)), |
831 | | get_minimum_or_exact_value(RHS,Properties,Card), |
832 | | number(Card), Card1 is Card+1, |
833 | | global_set_identifier(C,GlobalSet), |
834 | | get_texpr_type(C,set(global(GlobalSet))). |
835 | | is_greaterequalthan_card_global_set(TE,GlobalSet,_Properties,2) :- |
836 | | %% preferences:get_preference(allow_empty_global_sets,false), preference no longer exists |
837 | | /* GlobalSet /= { ANY } -> as GlobalSet cannot be empty & as ANY must be well-typed: |
838 | | we need at least one more element of GlobalSet */ |
839 | | get_texpr_expr(TE,not_equal(LHS,RHS)), |
840 | | ( get_texpr_expr(LHS,identifier(GlobalSet)) |
841 | | -> get_texpr_expr(RHS,set_extension([b(_X,global(GlobalSet),_)])) |
842 | | ; get_texpr_expr(RHS,identifier(GlobalSet)), |
843 | | get_texpr_expr(LHS,set_extension([b(_X,global(GlobalSet),_)])) |
844 | | ). |
845 | | %%print(not_equal(GlobalSet,_X)),nl. |
846 | | % 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))])) |
847 | | |
848 | | % get mininimum or exact value for an expression |
849 | | get_minimum_or_exact_value(TE,Properties,N) :- %print(get_min(TE,Properties)),nl,nl, |
850 | | get_texpr_expr(TE,E), |
851 | | get_minimum_or_exact_value_aux(E,Properties,N). |
852 | | get_minimum_or_exact_value_aux(integer(N),_,N). |
853 | | get_minimum_or_exact_value_aux(identifier(ID),Properties,N) :- |
854 | | (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 |
855 | | get_minimum_or_exact_value_aux(add(A,B),Properties,Res) :- |
856 | | get_minimum_or_exact_value(A,Properties,RA), |
857 | | get_minimum_or_exact_value(B,Properties,RB), Res is RA+RB. |
858 | | % TO DO: other operators |
859 | | get_minimum_or_exact_value_aux(card(Set),_Properties,Card) :- |
860 | | Set = b(identifier(GlobalSetName),set(global(GlobalSetName)),_), |
861 | | try_extract_setsize_from_machine(GlobalSetName,L,U), % will try extraction; unless a cycle is hit |
862 | | Card is U+1-L. |
863 | | |
864 | | extract_min_or_exact_value_for_id(ID,Properties,N) :- |
865 | | member_in_conjunction(TE,Properties), |
866 | | get_texpr_expr(TE,E), |
867 | | get_value_bound(E,ID,N). |
868 | | |
869 | | % equalities already inlined |
870 | | get_value_bound(equal(LHS,RHS),ID,N) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N). |
871 | | get_value_bound(equal(RHS,LHS),ID,N) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N). |
872 | | get_value_bound(greater(LHS,RHS),ID,N1) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N), N1 is N+1. |
873 | | get_value_bound(less(RHS,LHS),ID,N1) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N), N1 is N+1. |
874 | | get_value_bound(greater_equal(LHS,RHS),ID,N) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N). |
875 | | get_value_bound(less_equal(RHS,LHS),ID,N) :- get_texpr_id(LHS,ID), get_texpr_integer(RHS,N). |
876 | | |
877 | | get_texpr_integer(b(integer(N),_,_),N). |
878 | | |
879 | | |
880 | | :- volatile global_set_user_defined_scope/2. |
881 | | :- dynamic global_set_user_defined_scope/2. |
882 | | |
883 | | /* allow to set scope for specific sets, e.g., via command-line */ |
884 | | set_user_defined_scope(GS,X) :- |
885 | | \+ number(X),!, |
886 | | add_internal_error('Scope must be number: ',set_user_defined_scope(GS,X)). |
887 | | set_user_defined_scope(GS,X) :- |
888 | | format('% Setting user defined scope: ~w == ~w~n',[GS,X]), |
889 | | assert(global_set_user_defined_scope(GS,X)). |
890 | | |
891 | | get_user_defined_scope(GlobalSetName,LowBound,UpBound) :- |
892 | | (var(GlobalSetName) |
893 | | -> add_error(get_user_defined_scope,'Arg is variable: ',get_user_defined_scope) ; true), |
894 | | (global_set_user_defined_scope(GlobalSetName,UpBound) |
895 | | -> LowBound=1 |
896 | | ; extract_setsize_from_defs(GlobalSetName,LowBound,UpBound)). |
897 | | |
898 | | :- use_module(translate,[translate_bexpression/2]). |
899 | | extract_setsize_from_defs(GlobalSetName,LowBound,UpBound) :- |
900 | | b_get_machine_setscope(GlobalSetName,DefTerm), |
901 | | get_texpr_expr(DefTerm,DefExpr), |
902 | | (extract_integer_range(DefExpr,LowBound,UpBound) |
903 | | -> true |
904 | | ; translate_bexpression(DefTerm,DS), |
905 | | add_warning(extract_setsize_from_defs,'scope DEFINITION for deferred set should be number or interval: ',DS,DefTerm), |
906 | | fail |
907 | | ). |
908 | | |
909 | | extract_integer_range(interval(LB,UB), LowBound, UpBound) :- |
910 | | get_texpr_expr(LB,integer(LowBound)), get_texpr_expr(UB,integer(UpBound)). |
911 | | %extract_integer_range(set_extension(CstList),LowBound,UpBound) :- |
912 | | % LowBound = 1, length(CstList,UpBound). % extract names of constants in list for pretty printing; check that different names |
913 | | %extract_integer_range(value(avl_set(A)),LowBound,UpBound) :- |
914 | | % LowBound = 1, avl:avl_size(A,UpBound). |
915 | | extract_integer_range(integer(UpBound),LowBound,UpBound) :- |
916 | | LowBound = 1, UpBound>0. |
917 | | |
918 | | |
919 | | :- dynamic start_extracting_setsize/1. |
920 | | |
921 | | start_extracting_setsize_from_machine(GlobalSetName) :- %print(start(GlobalSetName)),nl, |
922 | | (start_extracting_setsize(GlobalSetName) |
923 | | -> add_internal_error('Cyclic computation: ',start_extracting_setsize_from_machine(GlobalSetName)) |
924 | | ; assert(start_extracting_setsize(GlobalSetName))). |
925 | | |
926 | | assert_extract_setsize_from_machine_cache(GlobalSetName,LowBound,UpBound) :- |
927 | | retractall(start_extracting_setsize(GlobalSetName)), |
928 | | %print(finished(GlobalSetName,LowBound,UpBound)),nl, |
929 | | (retract(extract_setsize_from_machine_cache(GlobalSetName,L,U)) |
930 | | -> print(overriding_set_size(GlobalSetName,LowBound-UpBound,L-U)),nl |
931 | | ; true), |
932 | | assert(extract_setsize_from_machine_cache(GlobalSetName,LowBound,UpBound)), |
933 | | (UpBound<LowBound |
934 | | -> format_error('*** illegal empty global set ~w, bounds ~w:~w~n',[GlobalSetName,LowBound,UpBound]) |
935 | | ; true |
936 | | ). |
937 | | |
938 | | |
939 | | %% b_try_update_fd_cardinality has been removed |
940 | | |
941 | | :- volatile enumerated_set/1, fixed_deferred_set_size/2. |
942 | | :- dynamic enumerated_set/1, fixed_deferred_set_size/2. |
943 | | % true if GS is a deferred set whose size was not fixed; i.e., ProB has not inspected all possible instantiations |
944 | | unfixed_deferred_set(GS) :- |
945 | | b_global_set(GS), \+ enumerated_set(GS), \+ fixed_deferred_set_size(GS,_). |
946 | | |
947 | | contains_unfixed_deferred_set(Type) :- unfixed_deferred_set(Type). |
948 | | contains_unfixed_deferred_set(global(Type)) :- contains_unfixed_deferred_set(Type). |
949 | | contains_unfixed_deferred_set(set(Type)) :- contains_unfixed_deferred_set(Type). |
950 | | contains_unfixed_deferred_set(seq(Type)) :- contains_unfixed_deferred_set(Type). |
951 | | contains_unfixed_deferred_set(couple(Type1,Type2)) :- |
952 | | contains_unfixed_deferred_set(Type1) ; contains_unfixed_deferred_set(Type2). |
953 | | contains_unfixed_deferred_set(record(Fields)) :- |
954 | | ((member(field(_,Type),Fields), contains_unfixed_deferred_set(Type)) -> true). |
955 | | |
956 | | :- assert_pre(b_global_sets:assert_enumerated_set(_GS,_L,_,_),true). |
957 | | % TODO(DP, 7.8.2008) |
958 | | % (atomic(GS),type_check(L,list(xml_identifier)))). |
959 | | :- assert_post(b_global_sets:assert_enumerated_set(_,_,L,U),(number(L),number(U))). |
960 | | |
961 | | assert_enumerated_set(GlobalSetName,ListOfConstants,LowBound,UpBound) :- |
962 | | assert(enumerated_set(GlobalSetName)), |
963 | | % print_message(enum_set(GlobalSetName,ListOfConstants)), |
964 | | LowBound = 1, |
965 | | length(ListOfConstants,UpBound), |
966 | | assert_extract_setsize_from_machine_cache(GlobalSetName,LowBound,UpBound), |
967 | | add_named_constants_to_global_set(GlobalSetName,ListOfConstants). |
968 | | |
969 | | add_named_constants_to_global_set(GlobalSetName,ListOfConstants) :- |
970 | | (b_global_constant(GlobalSetName,CurNr,CurCst) |
971 | | -> add_error_fail(add_named_constants_to_global_set,'Globalset already has constants: ', |
972 | | b_global_constant(GlobalSetName,CurNr,CurCst)) |
973 | | ; true), |
974 | ? | nth1(Nr,ListOfConstants,Cst), |
975 | | (b_global_constant(GS2,_,Cst) |
976 | | -> (GS2=GlobalSetName |
977 | | -> add_internal_error('Duplicate element in global set:',(Cst,GS2)) % error is already caught by type checker |
978 | | ; add_internal_error('Element appears in multiple sets:',(Cst,GS2,GlobalSetName)) % ditto |
979 | | ) |
980 | | ; true |
981 | | ), |
982 | | assert(b_global_constant(GlobalSetName,Nr,Cst)), |
983 | | assert(lookup_global_constant(Cst,fd(Nr,GlobalSetName))), |
984 | | fail. |
985 | | add_named_constants_to_global_set(_,_). |
986 | | |
987 | | |
988 | | %add_global_set_with_named_constants(GlobalSetName,ListOfConstants) :- |
989 | | % ~~pp_mnf(b_global_sets:assert_enumerated_set(GlobalSetName,ListOfConstants,_,_)), |
990 | | % add_new_global_set(GlobalSetName). |
991 | | |
992 | | |
993 | | |
994 | | :- assert_pre(b_global_sets:b_extract_fd_type(_G,_L,_U),true). |
995 | | :- assert_post(b_global_sets:b_extract_fd_type(G,L,U),(atomic(G),(integer(L),integer(U)))). |
996 | | |
997 | | b_extract_fd_type(GlobalSetName,LowBound,UpBound) :- |
998 | ? | b_global_set(GlobalSetName), % was b_get_machine_set(GlobalSetName), |
999 | | extract_setsize_from_machine(GlobalSetName,LowBound,UpBound). |
1000 | | |
1001 | | is_b_precompiled_globalset(GlobalSetName) :- extract_setsize_from_machine_cache(GlobalSetName,_,_). |
1002 | | |
1003 | | % like b_extract_fd_type above, but with GS known (avoids backtracking b_supplementary_global_set) |
1004 | | b_get_fd_type_bounds(NonvarGlobalSetname,LowBound,UpBound) :- nonvar(NonvarGlobalSetname), |
1005 | | extract_setsize_from_machine_cache(NonvarGlobalSetname,L,U), |
1006 | | !, |
1007 | | (LowBound,UpBound)=(L,U). |
1008 | | b_get_fd_type_bounds(GS,L,U) :- add_internal_error('Illegal call: ',b_get_fd_type_bounds(GS,L,U)),fail. |
1009 | | |
1010 | | b_global_set_cardinality('STRING',Card) :- !, Card=inf. |
1011 | | b_global_set_cardinality('NAT',Card) :- !, get_preference(maxint,MaxInt), Card is MaxInt+1. |
1012 | | b_global_set_cardinality('NATURAL',Card) :- !, Card=inf. %get_preference(maxint,MaxInt), Card is MaxInt+1. |
1013 | | b_global_set_cardinality('NAT1',Card) :- !, get_preference(maxint,Card). |
1014 | | b_global_set_cardinality('NATURAL1',Card) :- !, Card=inf. % get_preference(maxint,Card). |
1015 | | b_global_set_cardinality('INT',Card) :- !, get_preference(maxint,MaxInt), |
1016 | | get_preference(minint,MinInt), |
1017 | | (MaxInt >= MinInt -> Card is MaxInt-MinInt+1 |
1018 | | ; add_error(b_global_set_cardinality,'MININT larger than MAXINT',(MinInt:MaxInt)), |
1019 | | Card = 0 |
1020 | | ). |
1021 | | b_global_set_cardinality('INTEGER',Card) :- !, Card=inf. % b_global_set_cardinality('INT',Card). |
1022 | | b_global_set_cardinality(GlobalSet,Card) :- nonvar(GlobalSet),!, |
1023 | | (b_get_fd_type_bounds(GlobalSet,Low,Up) -> Card is 1+ Up - Low |
1024 | | ; add_internal_error('Unknown global set: ', b_global_set_cardinality(GlobalSet,Card)), |
1025 | | fail). |
1026 | | b_global_set_cardinality(GlobalSet,Card) :- %b_global_set(GlobalSet), |
1027 | | b_extract_fd_type(GlobalSet,Low,Up), Card is 1+ Up - Low. |
1028 | | % enum_warning_for_global_set(GlobalSet,'assuming deferred set to be of given cardinality',Card,trigger_true(card)). |
1029 | | %% Note: The Disprover now checks whether unfixed_deferred sets were involved; keeping track of implicit enumerations of deferred sets is just too difficult |
1030 | | |
1031 | | b_fd_card(GlobalSet,Card) :- b_get_fd_type_bounds(GlobalSet,Low,Up), Card is 1+ Up - Low. |
1032 | | |
1033 | | b_non_empty_global_set(_). % allow_empty_global_sets no longer exists; all global_sets are non-empty |
1034 | | %b_non_empty_global_set(IntSet) :- b_integer_or_string_set(IntSet). |
1035 | | %b_non_empty_global_set(GlobalSet) :- b_global_set(GlobalSet), |
1036 | | % b_get_fd_type_bounds(GlobalSet,Low,Up), Up >= Low. |
1037 | | |
1038 | | b_empty_global_set(_) :- fail. % allow_empty_global_sets no longer exists; all global_sets are non-empty |
1039 | | %b_empty_global_set(GlobalSet) :- %b_global_set(GlobalSet), |
1040 | | % b_get_fd_type_bounds(GlobalSet,Low,Up), Up < Low. |
1041 | | |
1042 | | |
1043 | | b_integer_or_string_set('STRING'). |
1044 | | b_integer_or_string_set(X) :- b_integer_set(X). |
1045 | | |
1046 | | b_integer_set('NAT'). b_integer_set('NATURAL'). |
1047 | | b_integer_set('NAT1'). b_integer_set('NATURAL1'). |
1048 | | b_integer_set('INT'). b_integer_set('INTEGER'). |
1049 | | |
1050 | | % convert a type term into a global_set term |
1051 | | try_b_type2global_set(string,R) :- !, R= global_set('STRING'). |
1052 | | try_b_type2global_set(integer,R) :- !, R= global_set('INTEGER'). |
1053 | ? | try_b_type2global_set(global(GS),R) :- b_global_set(GS),!, R=global_set(GS). |
1054 | | try_b_type2global_set(freetype(ID),R) :- !, R=freetype(ID). |
1055 | | try_b_type2global_set(boolean,R) :- !, |
1056 | | %custom_explicit_sets:construct_avl_from_lists([pred_false /* bool_false */,pred_true /* bool_true */],R). |
1057 | | R = avl_set(node(pred_false,true,1,empty,node(pred_true,true,0,empty,empty))). |
1058 | | |
1059 | | b_type2_set(GS,Res) :- try_b_type2global_set(GS,GR),!, Res=GR. |
1060 | | b_type2_set(Type,Res) :- Res = closure(['_zzzz_unary'],[Type],b(truth,pred,[])). |
1061 | | %b_type2global_set(GS,_) :- add_error_and_fail(b_type2_set,'Type cannot be converted to global_set: ',GS). |
1062 | | |
1063 | | %:- use_module(fd_utils). |
1064 | | :- use_module(fd_utils_clpfd). |
1065 | | |
1066 | | global_type(fd(X,GlobalSet),GlobalSet) :- %b_global_set(GlobalSet), |
1067 | | if(b_get_fd_type_bounds(GlobalSet,Low,Up), %print(b_get_fd_type_bounds(X,GlobalSet,Low,Up)),nl, when(nonvar(X),trace), |
1068 | | % print(in_fd(X,Low,Up)),nl, |
1069 | | in_fd(X,Low,Up), |
1070 | | add_internal_error('Illegal global set: ',global_type(fd(X,GlobalSet),GlobalSet))). |
1071 | | global_type2(X,GlobalSet) :- %b_global_set(GlobalSet), |
1072 | | if(b_get_fd_type_bounds(GlobalSet,Low,Up), |
1073 | | in_fd(X,Low,Up), |
1074 | | add_internal_error('Illegal global set: ',global_type2(X,GlobalSet))). |
1075 | | |
1076 | | global_type_wf(fd(X,GlobalSet),GlobalSet,WF) :- %b_global_set(GlobalSet), |
1077 | | b_get_fd_type_bounds(GlobalSet,Low,Up), %print(b_get_fd_type_bounds(X,GlobalSet,Low,Up)),nl, |
1078 | | in_fd_wf(X,Low,Up,WF). |
1079 | | |
1080 | | % get value and setup global_type FD constraint if it is a variable |
1081 | | get_global_type_value(FD,Type,X) :- |
1082 | | (var(FD), % print(fresh(Type,FD)),nl, |
1083 | | nonvar(Type) -> global_type2(X,Type) % instantiate FD bounds if we create a fresh FD variable X |
1084 | | ; true), |
1085 | | FD = fd(X,Type). |
1086 | | |
1087 | | % like enum_global_type but with a nonvar GlobalSet + generate enum_warning for deferred set (currently commented out) |
1088 | | enumerate_global_type_with_enum_warning(R,GlobalSet,_EnumWarning) :- var(R),!, % print(enum_var(R,GlobalSet)),nl, |
1089 | | b_get_fd_type_bounds(GlobalSet,Low,Up), % first setup CLP(FD) bounds before instantiating |
1090 | | in_fd(X,Low,Up), |
1091 | | R=fd(X,GlobalSet), |
1092 | | enum_fd(X,Low,Up). |
1093 | | enumerate_global_type_with_enum_warning(fd(X,GlobalSet),GlobalSet,_EnumWarning) :- |
1094 | ? | (nonvar(X) -> true |
1095 | ? | ; b_get_fd_type_bounds(GlobalSet,Low,Up), |
1096 | | %enum_warning_for_global_set(GlobalSet,'assuming deferred set to be of finite cardinality','?',EnumWarning), |
1097 | ? | enum_fd(X,Low,Up) |
1098 | | ). |
1099 | | |
1100 | | |
1101 | | enum_global_type(fd(X,GlobalSet),GlobalSet) :- %b_global_set(GlobalSet), |
1102 | ? | (nonvar(GlobalSet) -> |
1103 | | (b_get_fd_type_bounds(GlobalSet,Low,Up) -> enum_global_type_aux(X,Low,Up,GlobalSet) |
1104 | | ; add_internal_error('Illegal value: ',enum_global_type(fd(X,GlobalSet),GlobalSet)),fail) |
1105 | ? | ; b_global_set(GlobalSet), |
1106 | ? | b_get_fd_type_bounds(GlobalSet,Low,Up), |
1107 | ? | enum_global_type_aux(X,Low,Up,GlobalSet) |
1108 | | ). |
1109 | | |
1110 | | enum_global_type_aux(X,Low,Up,GlobalSet) :- |
1111 | | atomic(X), |
1112 | | !, |
1113 | | ((number(X),X >= Low, X =< Up) |
1114 | | -> true |
1115 | | ; add_internal_error('Illegal value: ', enum_global_type_aux(X,Low,Up,GlobalSet))). |
1116 | | enum_global_type_aux(X,Low,Up,_GlobalSet) :- |
1117 | ? | enum_fd(X,Low,Up). |
1118 | | |
1119 | | % get range information for global set and generate warning if necessary |
1120 | | %b_fd_type_with_enum_warning(GlobalSet,Low,Up) :- |
1121 | | % b_fd_type(GlobalSet,Low,Up), |
1122 | | % enum_warning_for_global_set(GlobalSet,'assuming deferred set to be of finite cardinality',Up,trigger_true(card)). |
1123 | | |
1124 | | /* ------------------------------------- */ |
1125 | | |
1126 | | |
1127 | | :- block all_elements_of_type(-,?). |
1128 | | all_elements_of_type(Type,Set) :- % print(all_elements(Type,Set)),nl, |
1129 | | all_elements_of_type2(Type,Set). |
1130 | | |
1131 | | :- use_module(kernel_objects,[all_strings/1, enum_warning/5]). |
1132 | | all_elements_of_type2('STRING',Res) :- !, |
1133 | | all_strings(Res). |
1134 | | all_elements_of_type2('NAT',Res) :- !, get_preference(maxint,MaxInt), |
1135 | | gen_int_set(0,MaxInt,Res). |
1136 | | all_elements_of_type2('NATURAL',Res) :- !, get_preference(maxint,MaxInt), |
1137 | | enum_warning('NATURAL',0:sup,0:MaxInt,trigger_throw('NATURAL'),unknown), %print('### Warning: expanding NATURAL'),nl, |
1138 | | gen_int_set(0,MaxInt,Res). |
1139 | | all_elements_of_type2('NAT1',Res) :- !, get_preference(maxint,MaxInt), |
1140 | | gen_int_set(1,MaxInt,Res). |
1141 | | all_elements_of_type2('NATURAL1',Res) :- !, |
1142 | | get_preference(maxint,MaxInt), |
1143 | | enum_warning('NATURAL1',1:sup,1:MaxInt,trigger_throw('NATURAL1'),unknown), %print('### Warning: expanding NATURAL1'),nl, |
1144 | | gen_int_set(1,MaxInt,Res). |
1145 | | all_elements_of_type2('INT',Res) :- !, get_preference(maxint,MaxInt), |
1146 | | get_preference(minint,MinInt), |
1147 | | gen_int_set(MinInt,MaxInt,Res). |
1148 | | all_elements_of_type2('INTEGER',Res) :- !, get_preference(maxint,MaxInt), |
1149 | | get_preference(minint,MinInt), |
1150 | | enum_warning('INTEGER',inf:sup,MinInt:MaxInt,trigger_throw('INTEGER'),unknown), %print('### Warning: expanding INTEGER'),nl, |
1151 | | gen_int_set(MinInt,MaxInt,Res). |
1152 | | all_elements_of_type2(Type,Set) :- |
1153 | | b_get_fd_type_bounds(Type,Low,Up), |
1154 | | % ensure we do not use Random enumeration |
1155 | | findall(fd(El,Type),enum_fd_linear(El,Low,Up),Set). |
1156 | | |
1157 | | % a version of all_elements_of_type which may randomise element order if preference set |
1158 | | :- block all_elements_of_type_rand(-,?). |
1159 | | all_elements_of_type_rand(Type,Set) :- % print(all_elements(Type,Set)),nl, |
1160 | | all_elements_of_type_rand2(Type,Set). |
1161 | | |
1162 | | all_elements_of_type_rand2(Type,Set) :- b_global_set(Type),!, |
1163 | | b_get_fd_type_bounds(Type,Low,Up), |
1164 | | % may use Random enumeration if preference RANDOMISE_ENUMERATION_ORDER set |
1165 | | findall(fd(El,Type),enum_fd(El,Low,Up),Set). |
1166 | | all_elements_of_type_rand2(Type,Set) :- % TO DO: we could permute the solution here; but currently all_elements_of_type_rand2 is only called for enumerated/deferred sets |
1167 | | all_elements_of_type2(Type,Set). |
1168 | | |
1169 | | |
1170 | | gen_int_set(Min,Max,Set) :- |
1171 | | ((Min>Max) -> (Set = []) |
1172 | | ; (Set = [int(Min)|RSet], |
1173 | | M1 is Min+1, |
1174 | | gen_int_set(M1,Max,RSet))). |