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