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(clpfd_interface,[try_post_constraint/1, post_constraint/2, post_constraint2/2, |
6 | | force_post_constraint/1, |
7 | | clpfd_neq_expr/2, clpfd_eq_expr/2, clpfd_eq_expr_optimized/2, |
8 | | clpfd_eq_div/3, clpfd_eq_fdiv/3, |
9 | | clpfd_eq_guarded_div/3, clpfd_eq_guarded_fdiv/3, |
10 | | clpfd_eq/2, clpfd_neq/2, |
11 | | clpfd_geq2/3, |
12 | | clpfd_geq/3, clpfd_gt/3, clpfd_leq/3, clpfd_lt/3, |
13 | | clpfd_leq_expr/2, clpfd_lt_expr/2, |
14 | | clpfd_inrange/3, clpfd_inrange/4, |
15 | | clpfd_not_inrange/3, clpfd_not_in_non_empty_range/3, |
16 | | clpfd_sum/2, |
17 | | clpfd_inlist/2, clpfd_not_inlist/2, clpfd_reify_inlist/3, force_clpfd_inlist/2, |
18 | | clpfd_minimum/2, clpfd_maximum/2, |
19 | | clpfd_if_then_else/4, |
20 | | clpfd_domain/3, clpfd_size/2, |
21 | | clpfd_some_element_in_domain/2, |
22 | | clpfd_max_bounded/1, |
23 | | clpfd_can_match/2, |
24 | | clpfd_check_geq_nr/2, |
25 | | clpfd_alldifferent/1, |
26 | | %clpfd_labeling/1, % not used at the moment |
27 | | clpfd_degree/2, |
28 | | clpfd_in_domain/1, clpfd_labeling/2, |
29 | | clpfd_can_intersect/2, |
30 | | %clpfd_reverse_labeling/1, clpfd_reverse_labeling/2, |
31 | | clpfd_get_next_variable_to_label/2, clpfd_get_next_variable_to_label/3, |
32 | | %clpfd_get_next_variable_to_label_ffc/3, |
33 | | is_64_bit_system/0, clpfd_overflow_error_message/0, clpfd_overflow_warning_message/0, |
34 | | integer_too_large_for_clpfd/1, integer_too_small_for_clpfd/1, |
35 | | catch_clpfd_overflow_call1/1, catch_clpfd_overflow_call2/2, |
36 | | computable_arith_expression/1, |
37 | | clpfd_randomised_labeling/2, clpfd_randomised_in_domain/1]). |
38 | | |
39 | | :- use_module(library(clpfd)). |
40 | | :- load_files(library(system), [when(compile_time), imports([environ/2])]). |
41 | | :- if(\+ environ(disable_chr, true)). |
42 | | :- use_module(chrsrc(chr_integer_inequality)). |
43 | | :- endif. |
44 | | |
45 | | |
46 | | :- if((current_prolog_flag(version_data,sicstus(4,X,Y,_,_)),X<3,Y<3)). |
47 | | % -------- patch provided by SICS ----------- |
48 | | % solves lost solution in CLP(FD) when calling: |
49 | | % when(nonvar(X),member(Y,[1,2])), X in 1..2, X in 2..3. |
50 | | % patch provided 23rd June 2012 for SICS 4.2.1 |
51 | | % bug cause ProB to miss solutions in e.g. following set comprehension: |
52 | | % {xx | xx:{1|->TRUE,2|->TRUE,2|->FALSE} & (xx : {(2|->TRUE),(2|->FALSE),(3|->TRUE)} or xx : {(1|->TRUE),(1|->FALSE),(2|->TRUE),(2|->FALSE),(3|->TRUE),(3|->FALSE)})} |
53 | | :- set_prolog_flag(redefine_warnings,proceed). |
54 | | clpfd:propagate_interval_chk(Pruned, Min, Max) :- |
55 | | clpfd:'$fd_in_interval'(Pruned, Min, Max, 1), !, % 20120623 |
56 | | clpfd:'$fd_evaluate_indexical'(RC, Global), |
57 | | clpfd:evaluate(RC, Global). |
58 | | clpfd:propagate_interval_chk(Pruned, Min, Max) :- |
59 | | clpfd:arg_attribute(Pruned, _, in(Pruned,Min..Max), 1), !, |
60 | | fail. |
61 | | clpfd:propagate_interval_chk(_, _, _). |
62 | | |
63 | | clpfd:prune_and_propagate_chk(Pruned, Set) :- |
64 | | clpfd:'$fd_in_set'(Pruned, Set, 1), !, % 20120623 |
65 | | clpfd:'$fd_evaluate_indexical'(RC, Global), |
66 | | clpfd:evaluate(RC, Global). |
67 | | clpfd:prune_and_propagate_chk(Pruned, Set) :- |
68 | | clpfd:arg_attribute(Pruned, _, in_set(Pruned,Set), 1), !, |
69 | | fail. |
70 | | clpfd:prune_and_propagate_chk(_, _). |
71 | | %:- set_prolog_flag(redefine_warnings,on). |
72 | | % ---------- end patch --------------- |
73 | | :- endif. |
74 | | |
75 | | |
76 | | %:- use_module(typechecker). |
77 | | :- use_module(tools). |
78 | | :- use_module(preferences). |
79 | | :- use_module(library(timeout)). |
80 | | :- use_module(error_manager). |
81 | | :- use_module(self_check). |
82 | | |
83 | | :- use_module(module_information,[module_info/2]). |
84 | | :- module_info(group,kernel). |
85 | | :- module_info(description,'Provide interface to CLP(FD), with Prolog fallback in case of errors or when CLP(FD) turned off.'). |
86 | | |
87 | | :- meta_predicate try_post_constraint(0). |
88 | | :- meta_predicate post_constraint(0,0). |
89 | | :- meta_predicate post_constraint2(0,*). |
90 | | :- meta_predicate time_out_constraint(0,0). |
91 | | %:- meta_predicate force_post_constraint(0). |
92 | | %:- meta_predicate force_post_constraint(0,0). |
93 | | |
94 | | |
95 | | % Note: clpfd_eq is sometimes called with arithmetic expressions (treatment of plus/minus/...) |
96 | | % does not do the computable_arith_expression check |
97 | | clpfd_eq(E1,E2) :- simple(E1), simple(E2), % was (before 29.8.2014): number(E1),number(E2), |
98 | | !, |
99 | | E1=E2. |
100 | | clpfd_eq(E1,E2) :- |
101 | | (preferences:preference(use_chr_solver,true) -> chr_eq(E1,E2) ; true), |
102 | | try_post_constraint( E1 #= E2 ). |
103 | | |
104 | | clpfd_neq(E1,E2) :- number(E1),!, |
105 | | (number(E2) -> E1\=E2 ; try_post_constraint(E1 #\= E2),dif(E1,E2)). % no need to post dif constraint if one arg is number in clpfd mode |
106 | | clpfd_neq(E1,E2) :- number(E2),!, % no need to post dif constraint in clpfd mode |
107 | | post_constraint( E1 #\= E2, dif(E1,E2)). |
108 | | clpfd_neq(E1,E2) :- |
109 | | % Note: | ?- X #\= Y, Y #=X. --> does not fail; hence we also execute dif(E1,E2) in CLPFD mode ! |
110 | | % We might be able to skip calling dif, if we enable the CHR solver by default |
111 | | % Let's try it: only set up dif if CHR is disabled! |
112 | | (preferences:preference(use_chr_solver,true) -> chr_neq(E1,E2) ; dif(E1,E2)), |
113 | | try_post_constraint(E1 #\= E2). |
114 | | % clpfd_domain(E1,D1,D2), clpfd_domain(E2,B1,B2), print(dom(D1:D2,B1:B2)),nl. |
115 | | |
116 | | % set up a dif co-routine only if necessary; assumption: E1,E2 are either number or variable |
117 | | dif_when_necesssary(E1,E2) :- %print(dif_when_necesssary(E1,E2)),nl, |
118 | | var(E1),var(E2),!, dif(E1,E2). |
119 | | dif_when_necesssary(_,_). |
120 | | |
121 | | % this will force Y to be nonzero |
122 | | clpfd_eq_div(E1,X,Y) :- post_constraint( E1 #= X/Y, prolog_eq(E1,X//Y)), % truncated division -1 // 4 = 0 |
123 | | propagate_div_eq_eq(E1,X,Y). |
124 | | clpfd_eq_fdiv(E1,X,Y) :- post_constraint( E1 #= X div Y, prolog_eq(E1,X div Y)). % floored division -1 div 4 = -1 |
125 | | |
126 | | % certain == propagation rules not done by CLPFD |
127 | | propagate_div_eq_eq(E1,X,Y) :- |
128 | | (var(E1),X==Y |
129 | | -> %print(setting1(E1,X,Y)),nl, |
130 | | E1=1 % X/X can only be 1 or undefined when X=0 |
131 | | ; true). |
132 | | % we could check if E1==X --> Y=1 if X /= 0 |
133 | | |
134 | | % this waits until we are sure Y is not zero |
135 | | clpfd_eq_guarded_div(E1,X,Y) :- |
136 | | post_constraint3((E1 #= X/YY,MY in 0..1, Y #= MY*YY), prolog_eq(E1,X//Y),Posted), % Y can either be 0 or equal to YY |
137 | | % this is better than the previous solution: |
138 | | % post_constraint(Y#\=0 #=> YY#=Y, prolog_eq(Y,YY)). |
139 | | % however, x:44..77 & E : 1..5555 & E = x/y & y:0..4 only restricts E to 1..77, not 11..77 |
140 | | % what is still missing is propagating upper bounds of Y to YY |
141 | | % Y in 0..4, M in 0..1, YY in 1..77, Y #=M*YY |
142 | | % with this propagation below, we at least obtain propagation for x:44..77 & E : 1..5555 & y:0..4 & E = x/y |
143 | | % what we need is continously propagating the bounds from Y to YY (but not vice versa for 0) |
144 | | % maybe we can achieve this using indexicals ?? |
145 | | (Posted==true |
146 | | -> fd_max(Y,YUp), (number(YUp) -> YY #=< YUp ; true), |
147 | | fd_min(Y,YLow), (number(YLow) -> YY #>= YLow ; true), |
148 | | propagate_div_eq_eq(E1,X,Y) |
149 | | ; true). |
150 | | |
151 | | % now a similar solution for floored division |
152 | | clpfd_eq_guarded_fdiv(E1,X,Y) :- |
153 | | post_constraint3((E1 #= X div YY,MY in 0..1, Y #= MY*YY), prolog_eq(E1,X div Y),_Posted). |
154 | | % TO DO: add propagation like above or using indexicals |
155 | | |
156 | | % do we need to instantiate YY if Y becomes 0 ?? |
157 | | %:- block copy_divisor(-,?). |
158 | | %copy_divisor(Y,Y). |
159 | | |
160 | | |
161 | | |
162 | | % we check whether we have simple expressions of the form X = Y OP Z |
163 | | % in this case it is better to call ProB's implementations, as they have stronger propagation |
164 | | % (e.g., 0 = X-Y => X=Y or 1 = X-Y => X/=Y) |
165 | | clpfd_eq_expr_optimized(CLPFD_Expr1,CLPFD_Expr2) :- |
166 | | (simple(CLPFD_Expr1) -> call_eq_expr1(CLPFD_Expr2,CLPFD_Expr1) |
167 | | ; simple(CLPFD_Expr2) -> call_eq_expr2(CLPFD_Expr1,CLPFD_Expr2) |
168 | | ; clpfd_eq_expr_nonsimple(CLPFD_Expr1,CLPFD_Expr2)). |
169 | | |
170 | | :- use_module(kernel_dif). |
171 | | call_eq_expr1(CLPFD_Expr2,CLPFD_Expr1) :- |
172 | | (simple(CLPFD_Expr2) -> %print(unify(CLPFD_Expr2,CLPFD_Expr1)),nl, translate:print_clpfd_variable(CLPFD_Expr2),nl, translate:print_clpfd_variable(CLPFD_Expr1),nl, |
173 | | % first check if we have a pending dif; unification could trigger CLPFD enumeration |
174 | | ((var(CLPFD_Expr1),kernel_dif:frozen_dif(CLPFD_Expr2,CLPFD_Expr1)) -> /* print(dif),nl,*/ fail ; true), |
175 | | CLPFD_Expr2=CLPFD_Expr1 |
176 | | ; call_eq_expr2(CLPFD_Expr2,CLPFD_Expr1)). |
177 | | |
178 | | % first arg not simple, second arg simple |
179 | | call_eq_expr2(CLPFD_Expr2,CLPFD_Expr1) :- CLPFD_Expr2 = -(A,B),simple(A),simple(B),!, |
180 | | %kernel_objects:int_minus(int(A),int(B),int(CLPFD_Expr1)). % has special propagation rules |
181 | | (number(A),number(B) -> CLPFD_Expr1 is CLPFD_Expr2 |
182 | | ; clpfd_eq_expr_nonsimple(CLPFD_Expr2,CLPFD_Expr1), |
183 | | propagate_zero(A,B,CLPFD_Expr1)). |
184 | | call_eq_expr2(CLPFD_Expr2,CLPFD_Expr1) :- CLPFD_Expr2 = +(A,B),simple(A),simple(B),!, |
185 | | (number(A),number(B) -> CLPFD_Expr1 is CLPFD_Expr2 |
186 | | ; clpfd_eq_expr_nonsimple(CLPFD_Expr1,CLPFD_Expr2), |
187 | | propagate_zero(CLPFD_Expr1,A,B)). |
188 | | %kernel_objects:int_plus(int(A),int(B),int(CLPFD_Expr1)). % has special propagation rules |
189 | | call_eq_expr2(CLPFD_Expr2,CLPFD_Expr1) :- |
190 | | (computable_arith_expression(CLPFD_Expr2) -> CLPFD_Expr1 is CLPFD_Expr2 |
191 | | ; clpfd_eq_expr_nonsimple(CLPFD_Expr1,CLPFD_Expr2)). |
192 | | |
193 | | % a propagation rule not done by CLP(FD) |
194 | | :- block propagate_zero(-,-,-). |
195 | | propagate_zero(Sum,A,B) :- % print(prop_zero(Sum,A,B)),nl, |
196 | | (A==0 -> Sum=B ; B==0 -> Sum=A ; true). |
197 | | |
198 | | % eq_expr takes expressions as arguments |
199 | | clpfd_eq_expr(E1,E2) :- simple(E1),!, |
200 | | (simple(E2) -> E1=E2 |
201 | | ; computable_arith_expression(E2) -> E1 is E2 |
202 | | ; clpfd_eq_expr_nonsimple(E1,E2)). |
203 | | clpfd_eq_expr(E1,E2) :- simple(E2),computable_arith_expression(E1),!, E2 is E1. |
204 | | clpfd_eq_expr(E1,E2) :- clpfd_eq_expr_nonsimple(E1,E2). |
205 | | |
206 | | clpfd_eq_expr_nonsimple(E1,E2) :- |
207 | | (preferences:preference(use_chr_solver,true) -> chr_eq(E1,E2) ; true), |
208 | | post_constraint( E1 #= E2, prolog_eq(E1,E2)). |
209 | | %% post_constraint( E1 #= E2). % Used instead of line below because of serious bug in SICStus 4.1.3 |
210 | | % BUG: X*X #= 100, X #= 9. succeeds ! |
211 | | |
212 | | :- assert_must_succeed((clpfd_interface:prolog_eq(X,2+1),X==3)). |
213 | | :- assert_must_succeed((clpfd_interface:prolog_eq(4-1,X),X==3)). |
214 | | :- assert_must_succeed((clpfd_interface:prolog_eq(5-2,(2*2)-Y),Y=1)). |
215 | | :- assert_must_fail((clpfd_interface:prolog_eq(5-2,(2*2)-Y),Y=0)). |
216 | | prolog_eq(A,B) :- simple(A),!, when(ground(B), A is B). |
217 | | prolog_eq(A,B) :- simple(B),!, when(ground(A), B is A). |
218 | | prolog_eq(A,B) :- when((ground(A),ground(B)),=:=(A,B)). |
219 | | |
220 | | computable_arith_expression(X) :- number(X),!. |
221 | | computable_arith_expression(X) :- var(X),!,fail. |
222 | | computable_arith_expression(-(X)) :- computable_arith_expression(X). |
223 | | computable_arith_expression(+(X,Y)) :- computable_arith_expression(X),computable_arith_expression(Y). |
224 | | computable_arith_expression(-(X,Y)) :- computable_arith_expression(X),computable_arith_expression(Y). |
225 | | computable_arith_expression(*(X,Y)) :- computable_arith_expression(X),computable_arith_expression(Y). |
226 | | |
227 | | clpfd_neq_expr(E1,E2) :- number(E1),number(E2),!,E1\=E2. |
228 | | clpfd_neq_expr(E1,E2) :- %print(neq_expr(E1,E2)),nl, |
229 | | var(E1), var(E2), !, |
230 | | (preferences:preference(use_chr_solver,true) -> chr_neq(E1,E2) ; dif(E1,E2)), |
231 | | try_post_constraint( E1 #\= E2 ). % does not matter if posted |
232 | | % Note: | ?- X #\= Y, Y #=X. --> does not fail; hence we also execute dif(E1,E2) in CLPFD mode ! |
233 | | clpfd_neq_expr(E1,E2) :- % print(neq_expr(E1,E2)),nl, |
234 | | post_constraint3( E1 #\= E2, prolog_neq(E1,E2), Posted), |
235 | | (Posted=true -> my_dif(E1,E2) ; true). |
236 | | |
237 | | :- assert_must_succeed((clpfd_interface:prolog_neq(X,2+1),X=2)). |
238 | | :- assert_must_fail((clpfd_interface:prolog_neq(X,2+1),X=3)). |
239 | | :- assert_must_succeed((clpfd_interface:prolog_neq(3,X),X=2)). |
240 | | :- assert_must_succeed((clpfd_interface:prolog_neq(5-2,(2*2)-Y),Y=2)). |
241 | | prolog_neq(A,B) :- simple(A),simple(B),!, dif(A,B). |
242 | | prolog_neq(A,B) :- A \== B, when((ground(A),ground(B)),=\=(A,B)). |
243 | | |
244 | ? | my_dif(A,B) :- (simple(A);simple(B)),!. % do we need to setup things like dif(X,0) ? |
245 | | my_dif(A,B) :- comm_assoc_dif(A,B). |
246 | | |
247 | | % note: here we do not block, we use \== now rather than waiting on variables to be bound |
248 | | comm_assoc_dif(A,B) :- var(A),!, A \== B. |
249 | | comm_assoc_dif(A,B) :- var(B),!, A \== B. |
250 | | comm_assoc_dif(*(A1,A2),*(B1,B2)) :- !, |
251 | | extract_factors2(A1,A2,FA,[]), sort(FA,SFA), |
252 | | extract_factors2(B1,B2,FB,[]), sort(FB,SFB), |
253 | | %print(comm_assoc_dif_mul(SFA,SFB)),nl, |
254 | | SFA \== SFB. |
255 | | comm_assoc_dif(+(A1,A2),+(B1,B2)) :- !, |
256 | | extract_terms2(A1,A2,FA,[]), sort(FA,SFA), |
257 | | extract_terms2(B1,B2,FB,[]), sort(FB,SFB), |
258 | | %print(comm_assoc_dif_sum(SFA,SFB)),nl, |
259 | | SFA \== SFB. |
260 | | comm_assoc_dif(-(A1),-(B1)) :- !, comm_assoc_dif(A1,B1). |
261 | | comm_assoc_dif(-(A1,A2),-(B1,B2)) :- !, |
262 | | (comm_assoc_dif(A1,B1) -> true ; comm_assoc_dif(A2,B2)). |
263 | | % note : exponentiation and division are not handed directly to CLPFD at the moment, so no need to check here: |
264 | | %comm_assoc_dif(**(A1,A2),**(B1,B2)) :- !, (comm_assoc_dif(A1,B1) -> true ; comm_assoc_dif(A2,B2)). |
265 | | comm_assoc_dif(A,B) :- A \== B. % ,((nonvar(A),nonvar(B)) -> nl,print(cadif(A,B)),nl ; true). |
266 | | |
267 | | extract_factors2(A,B) --> extract_factors(A), extract_factors(B). |
268 | | extract_factors(A) --> {var(A)},!, [A]. |
269 | | extract_factors(*(A,B)) --> !, extract_factors(A), extract_factors(B). |
270 | | extract_factors(+(A,B)) --> !, [sum(ST)], {extract_terms2(A,B,T,[]),sort(T,ST)}. |
271 | | extract_factors(A) --> [A]. |
272 | | |
273 | | extract_terms2(A,B) --> extract_terms(A), extract_terms(B). |
274 | | extract_terms(A) --> {var(A)},!, [A]. |
275 | | extract_terms(+(A,B)) --> !, extract_terms(A), extract_terms(B). |
276 | | extract_terms(*(A,B)) --> !, [mul(ST)], {extract_factors2(A,B,T,[]),sort(T,ST)}. |
277 | | extract_terms(A) --> [A]. |
278 | | |
279 | | |
280 | | :- assert_must_succeed((clpfd_interface:clpfd_leq_expr(3,X+1),X=3)). |
281 | | :- assert_must_fail((clpfd_interface:clpfd_leq_expr(2,X-1),X=2)). |
282 | | clpfd_leq_expr(E1,E2) :- number(E1),number(E2), !, E1 =< E2. |
283 | | clpfd_leq_expr(E1,E2) :- useless_leq_constraint(E1,E2),!. |
284 | | clpfd_leq_expr(E1,E2) :- |
285 | | (preferences:preference(use_chr_solver,true) -> chr_leq(E1,E2) ; true), |
286 | | post_constraint( E1 #=< E2, prolog_leq(E1,E2)). |
287 | | prolog_leq(A,B) :- when((ground(A),ground(B)),=<(A,B)). |
288 | | |
289 | | :- assert_must_succeed((clpfd_interface:clpfd_lt_expr(3,X+1),X=3)). |
290 | | :- assert_must_fail((clpfd_interface:clpfd_lt_expr(2,X-1),X=3)). |
291 | | clpfd_lt_expr(E1,E2) :- number(E1),number(E2), !, E1 < E2. |
292 | | clpfd_lt_expr(E1,E2) :- useless_lt_constraint(E1,E2),!. |
293 | | clpfd_lt_expr(E1,E2) :- |
294 | | (preferences:preference(use_chr_solver,true) -> chr_lt(E1,E2) ; dif_when_necesssary(E1,E2)), % dif useful to detect that x<y & x=y is inconsisent; or x+1 < y+1 & x=y; note however x+1 < y & x=y is not detected as inconsistent without CHR |
295 | | post_constraint( E1 #< E2, prolog_lt(E1,E2)). |
296 | | prolog_lt(A,B) :- when((ground(A),ground(B)),<(A,B)). |
297 | | |
298 | | /* TO DO: do custom equalities div, plus, ... |
299 | | mydiv(X, Y, Z) :- |
300 | | (Y#=0 #\/ (Y#\=0 #/\ X/Y #= Z)). */ |
301 | | |
302 | | % a version of geq which avoids posting constraints if possible |
303 | | clpfd_geq2(E1,E2,Posted) :- number(E1),number(E2),!, E1 >= E2, Posted=true. |
304 | | clpfd_geq2(E1,E2,Posted) :- |
305 | | (preferences:preference(use_chr_solver,true) -> chr_leq(E2,E1) ; true), |
306 | | %E1 #>= E2, Posted=true. |
307 | | post_constraint2( E1 #>= E2 , Posted). |
308 | | |
309 | | clpfd_geq(E1,E2,Posted) :- clpfd_leq(E2,E1,Posted). |
310 | | clpfd_gt(E1,E2,Posted) :- clpfd_lt(E2,E1,Posted). |
311 | | |
312 | | clpfd_leq(E1,E2,Posted) :- |
313 | | useless_leq_constraint(E1,E2), |
314 | | !, Posted=true. % we have a useless constraint which is already known; avoid overhead of on_exception and time_out |
315 | | clpfd_leq(E1,E2,Posted) :- |
316 | | (preferences:preference(use_chr_solver,true) -> chr_leq(E1,E2) ; true), |
317 | | %E1 #=< E2, Posted=true. |
318 | | post_constraint2( E1 #=< E2 , Posted). |
319 | | |
320 | | useless_leq_constraint(E1,E2) :- number(E1), !, var(E2), fd_min(E2,Low), number(Low),E1 =< Low. |
321 | | useless_leq_constraint(E1,E2) :- number(E2), var(E1), fd_max(E1,Up), number(Up),Up =< E2. |
322 | | |
323 | | |
324 | | clpfd_lt(E1,E2,Posted) :- |
325 | | useless_lt_constraint(E1,E2), |
326 | | !, Posted=true. % we have a useless constraint which is already known; avoid overhead of on_exception and time_out |
327 | | clpfd_lt(E1,E2,Posted) :- |
328 | | (preferences:preference(use_chr_solver,true) -> chr_lt(E1,E2) ; dif_when_necesssary(E1,E2)), |
329 | | post_constraint2( E1 #< E2 , Posted). |
330 | | |
331 | | useless_lt_constraint(E1,E2) :- number(E1), !, var(E2), fd_min(E2,Low), number(Low),E1 < Low. |
332 | | useless_lt_constraint(E1,E2) :- number(E2), var(E1), fd_max(E1,Up), number(Up),Up < E2. |
333 | | |
334 | | clpfd_inrange(X,Low,Up) :- clpfd_inrange(X,Low,Up,_Posted). |
335 | | clpfd_inrange(X,Low,Up,Posted) :- |
336 | | (Low==Up -> X=Low, Posted=true |
337 | | ; (preferences:preference(use_chr_solver,true) |
338 | | -> chr_leq(Low,X), chr_leq(X,Up) |
339 | | ; true), |
340 | | (number(Low),number(Up) |
341 | | -> Low =< Up, % otherwise interval empty |
342 | | (number(X) -> X >= Low, X =< Up, Posted=true %, Low =<Up |
343 | | ; post_constraint2(X in Low..Up, Posted)) % Note: X in 1..Y, Y in 0..3. fails ! Range needs to be constant |
344 | | ; post_constraint2( (X #>=Low #/\ X #=< Up),Posted ) % #/\ Low #=< Up should be implied |
345 | | %,translate:print_clpfd_variables([X,Low,Up]),nl |
346 | | ) |
347 | | ). |
348 | | |
349 | | % assumes Low =< Up ! |
350 | | clpfd_not_in_non_empty_range(X,Low,Up) :- |
351 | ? | post_constraint((X #<Low #\/ X #> Up),outside_range_blocking(X,Low,Up)). |
352 | | |
353 | | :- block outside_range_blocking(-,?,?). |
354 | ? | outside_range_blocking(X,Low,Up) :- (X<Low ; X>Up). |
355 | | |
356 | | % assumes Low =< Up ! |
357 | | clpfd_not_inrange(X,Low,Up) :- preferences:preference(use_clpfd_solver,false), |
358 | | !, % avoid building up constraint below |
359 | | not_in_range_blocking(X,Low,Up). |
360 | | clpfd_not_inrange(X,Low,Up) :- |
361 | | % special treatment due to limitation of CLP(FD) disjunct: | ?- X #>10 #\/ X #>9. ---> X in inf..sup |
362 | | post_constraint2(XLow #<=> (X #<Low), Posted1), Posted1==true, |
363 | | post_constraint2(UpX #<=> (Up #< X), Posted2), Posted2==true, |
364 | | post_constraint2(UpLow #<=> (Up #< Low), Posted3), Posted3==true, |
365 | | !, |
366 | | clpfd_not_inrange_prop(XLow,UpX,UpLow,X,Low,Up). |
367 | | clpfd_not_inrange(X,Low,Up) :- |
368 | | force_post_constraint((X #<Low #\/ X #> Up #\/ Up #< Low),not_in_range_blocking(X,Low,Up)). |
369 | | |
370 | | :- block clpfd_not_inrange_prop(-,-,-,?,?,?). |
371 | | clpfd_not_inrange_prop(XLow,UpX,UpLow,_X,_Low,_Up) :- %print(unblock(XLow,UpX,UpLow,X,Low,Up)),nl, |
372 | ? | (XLow==1 ; UpX==1 ; UpLow==1),!. |
373 | | clpfd_not_inrange_prop(XLow,UpX,UpLow,X,Low,Up) :- XLow==0,!, |
374 | | post_constraint2(Up #< max(X,Low),_), disjunct_reify(UpX,UpLow). |
375 | | clpfd_not_inrange_prop(XLow,UpX,UpLow,X,Low,Up) :- UpX==0,!, |
376 | | post_constraint2(Low #> min(X,Up),_), disjunct_reify(XLow,UpLow). |
377 | | clpfd_not_inrange_prop(XLow,UpX,_UpLow,_X,_Low,_Up) :- % UpLow==0, |
378 | | % Anything more we can infer here ?? |
379 | | disjunct_reify(XLow,UpX). |
380 | | |
381 | | :- block disjunct_reify(-,-). |
382 | ? | disjunct_reify(UpX,UpLow) :- (UpX==1 ; UpLow==1),!. |
383 | | disjunct_reify(UpX,UpLow) :- (UpX==0 -> UpLow=1 ; UpX=1). |
384 | | |
385 | | |
386 | | :- block not_in_range_blocking(-,?,?),not_in_range_blocking(?,-,?). |
387 | | not_in_range_blocking(X,Y,Z) :- |
388 | | (X<Y -> true ; not_in_range_blocking2(X,Y,Z)). |
389 | | :- block not_in_range_blocking2(?,?,-). |
390 | | not_in_range_blocking2(X,Y,Z) :- (Z<Y -> true ; Z<X). |
391 | | |
392 | | clpfd_sum(List,Sum) :- |
393 | | clpfd_sum(List,Sum,Posted), |
394 | | (Posted==true -> true |
395 | | ; sum_list(List,int(0),int(Sum))). |
396 | | clpfd_sum(List,Sum,Posted) :- post_constraint2( sum(List,'#=',Sum),Posted). |
397 | | |
398 | | % non-CLPFD backup, when posting fails or CLPFD turned off |
399 | | :- block sum_list(-,?,?). |
400 | | sum_list([],Acc,Acc). |
401 | | sum_list([H|T],Acc,Res) :- kernel_objects:int_plus(int(H),Acc,NewAcc), sum_list(T,NewAcc,Res). |
402 | | |
403 | | |
404 | | % assert that a variable is one of the numbers in the FDList; FDList must be numbers, cannot be unbound FD variable |
405 | | % can fail due to overflows; one should not rely on it for soundness, see test 1353 |
406 | | clpfd_inlist(El,[X]) :- !, El=X. |
407 | | clpfd_inlist(El,FDList) :- |
408 | | try_post_constraint( (list_to_fdset(FDList,FDSET), El in_set FDSET)). |
409 | | |
410 | | % a version that always posts constraints independent of CLPFD preference |
411 | | force_clpfd_inlist(El,[X]) :- !, El=X. |
412 | | force_clpfd_inlist(El,FDList) :- |
413 | | list_to_fdset(FDList,FDSET), El in_set FDSET. |
414 | | |
415 | | % can fail due to overflows; one should not rely on it for soundness |
416 | | clpfd_not_inlist(El,FDList) :- |
417 | | try_post_constraint( (list_to_fdset(FDList,FDSET), fdset_complement(FDSET,C),El in_set C)). |
418 | | |
419 | | clpfd_maximum(El,List) :- %print(maximum(El,List)),nl, |
420 | | maximum(El,List). |
421 | | %force_post_constraint( maximum(El,List)). %, print(done(El)). |
422 | | clpfd_minimum(El,List) :- %print(minimum(El,List)),nl, |
423 | | %force_post_constraint( minimum(El,List)). |
424 | | minimum(El,List). |
425 | | |
426 | | |
427 | | clpfd_if_then_else(PredRes,ThenValue,ElseValue,Value) :- |
428 | | % TO DO: catch overflows and revert to simpler treatment then |
429 | | element(Idx,[ThenValue,ElseValue],Value), % Idx=1 if Value=ThenValue; Idx=2 if Value=ElseValue |
430 | | prop_12(PredRes,Idx). % if PredRes=pred_true -> Idx must be 1, 2 otherwise |
431 | | |
432 | | :- block prop_12(-,-). |
433 | | prop_12(P,V12) :- var(P),!,(V12=1 -> P=pred_true ; P=pred_false). |
434 | | prop_12(pred_true,1). |
435 | | prop_12(pred_false,2). |
436 | | |
437 | | /* |
438 | | on_exception(E,list_to_fdset([0,8589934592],R),true) |
439 | | E= error(domain_error(integer_list,[0,8589934592]),domain_error(list_to_fdset([0,8589934592],_A),1,integer_list,[0,8589934592])) |
440 | | domain_error |
441 | | */ |
442 | | |
443 | | |
444 | | clpfd_reify_inlist(El,FDList,FDRes) :- |
445 | | try_post_constraint( (list_to_fdset(FDList,FDSET), (El in_set FDSET) #<=> FDRes)). |
446 | | |
447 | | |
448 | | try_post_constraint(_) :- preferences:preference(use_clpfd_solver,false),!. |
449 | | :- if( environ(enable_time_out_for_constraints, true)). |
450 | | try_post_constraint(C) :- % print(posting(C)),nl, % |
451 | | on_exception(error(Err,_),time_out_constraint(C,true), check_error(Err,C) ). |
452 | | % Note: this will only catch exceptions that occur while posting ! |
453 | | % CLPFD integer overflow exceptions can still happen after posting :-( |
454 | | :- else. |
455 | | try_post_constraint(C) :- % print(posting(C)),nl, % |
456 | | on_exception(error(Err,_),C, check_error(Err,C) ). |
457 | | :- endif. |
458 | | |
459 | | post_constraint2(_,Posted) :- |
460 | | preferences:preference(use_clpfd_solver,false),!,Posted=false. |
461 | | :- if( environ(enable_time_out_for_constraints, true)). |
462 | | post_constraint2(C,Posted) :- % print(posting(C)),nl, % |
463 | | on_exception(error(Err,_),time_out_constraint(C,(Posted=false)), (check_error(Err,C),Posted=false) ), |
464 | | (Posted==false -> true ; Posted=true). |
465 | | :- else. |
466 | | post_constraint2(C,Posted) :- % print(posting(C)),nl, % |
467 | | on_exception(error(Err,_),(C,Posted=true), (check_error(Err,C),Posted=false) ). |
468 | | :- endif. |
469 | | |
470 | | |
471 | | check_error(representation_error(ErrMsg),C) :- |
472 | | is_clpfd_overflow_representation_error(ErrMsg), |
473 | | !, |
474 | | print_message('Ignoring constraint (integer overflow):'), |
475 | | print_message(C). |
476 | | check_error(domain_error(_Type,_Val),C) :- !, print_message('Ignoring constraint (domain error):'), |
477 | | % print(Type), print(' : '), print(Val), print(' : '), |
478 | | print_message(C). |
479 | | check_error(Err,C) :- |
480 | | %print_message('Ignoring constraint (unknown error):'), print(Err), print(' : '), print_message(C), |
481 | | add_error(clpfd_interface,'Ignoring constraint (unknown error):',Err:C). |
482 | | |
483 | | |
484 | | :- assert_must_succeed((print('Is 64-bit: '),(clpfd_interface:is_64_bit_system -> print('Yes') ; print('NO')),nl)). |
485 | | |
486 | | is_64_bit_system :- |
487 | | Err = error(representation_error(_),_), |
488 | | on_exception(Err, _X #= 268435456, fail). % 268435456 = 2^28, will generate overflow in 32-bit system |
489 | | |
490 | | |
491 | | clpfd_overflow_error_message :- clpfd_overflow_msg(M), |
492 | | add_error(clpfd_overflow,M). |
493 | | clpfd_overflow_warning_message :- clpfd_overflow_msg(M), |
494 | | add_warning(clpfd_overflow,M). |
495 | | %clpfd_overflow_print_message :- clpfd_overflow_msg(M),write(user_error,M),nl(user_error). |
496 | | |
497 | | clpfd_overflow_msg(M) :- clpfd_interface:is_64_bit_system, !, |
498 | | M = 'A CLPFD integer overflow occurred.\nSet CLPFD preference to FALSE (Animation Preferences).'. |
499 | | clpfd_overflow_msg('A CLPFD integer overflow occurred.\nSet CLPFD preference to FALSE (Animation Preferences) or use a 64 bit version of ProB.'). |
500 | | |
501 | | |
502 | | integer_too_small_for_clpfd(X) :- prolog_flag(min_tagged_integer,Min), X<Min. |
503 | | integer_too_large_for_clpfd(X) :- prolog_flag(max_tagged_integer,Max), X>Max. |
504 | | |
505 | | |
506 | | % post_constraint3(CLPFD,PrologBackup,Posted) |
507 | | post_constraint3(_,C,Posted) :- preferences:preference(use_clpfd_solver,false),!,Posted=false,call(C). |
508 | | post_constraint3(C,PrologBackup,Posted) :- % print(posting(C,PrologBackup)),nl, |
509 | | on_exception(error(Err,_), |
510 | | time_out_constraint((C,Posted=true),(Posted=false,PrologBackup)), |
511 | | (check_error(Err,C),Posted=false,call(PrologBackup)) ). |
512 | | |
513 | | % post_constraint(CLPFD,PrologBackup) |
514 | ? | post_constraint(_,C) :- preferences:preference(use_clpfd_solver,false),!,call(C). |
515 | | :- if(environ(enable_time_out_for_constraints, true)). |
516 | | post_constraint(C,PrologBackup) :- % print(posting(C,PrologBackup)),nl, |
517 | | on_exception(error(Err,_),time_out_constraint(C,PrologBackup), (check_error(Err,C),call(PrologBackup)) ). |
518 | | :- else. |
519 | | post_constraint(C,PrologBackup) :- % print(posting(C,PrologBackup)),nl,trace, |
520 | | on_exception(error(Err,_),C, (check_error(Err,C),call(PrologBackup)) ). |
521 | | :- endif. |
522 | | |
523 | | % no use_clpfd_solver check: |
524 | | force_post_constraint(C) :- call(C). % to use fd_batch(C) we need to convert C to list |
525 | | force_post_constraint(C,PrologBackup) :- % print(posting(C,PrologBackup)),nl, |
526 | | on_exception(error(Err,_),time_out_constraint(C,PrologBackup), (check_error(Err,C),call(PrologBackup)) ). |
527 | | |
528 | | |
529 | | :- if(\+ environ(enable_time_out_for_constraints, true)). |
530 | | time_out_constraint(C,_) :- call(C). % to use fd_batch(C) we need to convert C to list |
531 | | :- else. |
532 | | % Note: time_out/3 is quite expensive ! |
533 | | time_out_constraint(C,PrologBackup) :- %% debug:new_pp(C,PP), %% |
534 | | %%preferences:preference(time_out,CurTO), TO is 800 + (CurTO//100), time_out(C,TO,T), |
535 | | preferences:preference(solver_strength,Strength), |
536 | | TO is 1200 + Strength*200, |
537 | | time_out(C,TO,T), %% debug:new_sol(C,PP), %% |
538 | | (T==time_out -> print_message('Timeout when posting constraint:'), print_message(C), |
539 | | (preferences:preference(fail_if_clpfd_timeout,true) |
540 | | -> print_message('Assuming unsatisfiable !'),fail |
541 | | ; call(PrologBackup)) |
542 | | ; true). |
543 | | :- endif. |
544 | | |
545 | | |
546 | | :- assert_must_succeed((clpfd_interface:clpfd_domain(3,L,U), L==3, U==3)). |
547 | | clpfd_domain(Var,Low,Up) :- var(Var),!, |
548 | | fd_min(Var,Low), fd_max(Var,Up). |
549 | | % used to check (preferences:preference(use_clpfd_solver,false) -> Low=inf,Up=sup ; fd_min(Var,Low), fd_max(Var,Up) ). |
550 | | % but can also be called when CLPFD false for global_set values |
551 | | %used to call: fd_dom(Var, Low..Up)). fd_dom will sometimes return {1} \/ 2..3 or something like that ! |
552 | | clpfd_domain(X,X,X). |
553 | | |
554 | | % find some element in the domain of a FD variable |
555 | | clpfd_some_element_in_domain(Var,El) :- nonvar(Var),!, El=Var. |
556 | | clpfd_some_element_in_domain(Var,El) :- fd_min(Var,Low), number(Low),!,El=Low. |
557 | | clpfd_some_element_in_domain(Var,El) :- fd_max(Var,Up), number(Up),!,El=Up. |
558 | | clpfd_some_element_in_domain(Var,El) :- fd_set(Var,Set), fdset_parts(Set,Min,Max,_Rest), |
559 | | (number(Min) -> El=Min ; number(Max),El=Max). |
560 | | |
561 | | |
562 | | % succeed if we have a number or a CLPFD Var whose upper value is bounded |
563 | | clpfd_max_bounded(Var) :- var(Var), !, fd_max(Var,Up), number(Up). |
564 | | clpfd_max_bounded(N) :- number(N). |
565 | | |
566 | | :- assert_must_succeed(clpfd_interface:clpfd_can_match(1,1)). |
567 | | :- assert_must_succeed(clpfd_interface:clpfd_can_match(11,_X)). |
568 | | :- assert_must_succeed(clpfd_interface:clpfd_can_match(_X,22)). |
569 | | :- assert_must_succeed(clpfd_interface:clpfd_can_match(X,X)). |
570 | | :- assert_must_fail(clpfd_interface:clpfd_can_match(21,22)). |
571 | | :- assert_must_fail((dif(A,B),clpfd_interface:clpfd_can_match(A,B))). |
572 | | % check if two numbers or fd variables can match without unifying (as this could trigger propagations) |
573 | | clpfd_can_match(Var1,Var2) :- var(Var1),!, (var(Var2) -> clpfd_can_unify(Var1,Var2) ; clpfd_can_match_nr(Var1,Var2)). |
574 | | clpfd_can_match(Nr1,Var2) :- var(Var2),!,clpfd_can_match_nr(Var2,Nr1). |
575 | | clpfd_can_match(Nr1,Nr2) :- Nr1=Nr2. |
576 | | clpfd_can_match_nr(Var,Nr) :- % is it also worthwhile to check for frozen_dif here ? A #>10, B =15, B #\=A, clpfd_interface:clpfd_can_match(A,B) fails without it |
577 | | fd_set(Var,FDS), Nr in_set FDS. |
578 | | clpfd_can_unify(Var1,Var2) :- |
579 | | fd_set(Var1,FDS1), fd_set(Var2,FDS2), fdset_intersect(FDS1,FDS2), |
580 | | \+ kernel_dif:frozen_dif(Var1,Var2). |
581 | | |
582 | | % check if FD variables or numbers have common possible value |
583 | | clpfd_can_intersect(Number1,NrOrVar2) :- nonvar(Number1),!, |
584 | | (nonvar(NrOrVar2) -> Number1==NrOrVar2 |
585 | | ; fd_set(NrOrVar2,FDS2), fdset_member(Number1,FDS2)). |
586 | | clpfd_can_intersect(Var1,Number2) :- nonvar(Number2),!, |
587 | | fd_set(Var1,FDS1), fdset_member(Number2,FDS1). |
588 | | clpfd_can_intersect(Var1,Var2) :- |
589 | | fd_set(Var1,FDS1), fd_set(Var2,FDS2), fdset_intersect(FDS1,FDS2). |
590 | | |
591 | | % check if we can currently determine an integer to be greater or equal to a given number |
592 | | clpfd_check_geq_nr(X,Nr) :- |
593 | | number(X),!, |
594 | | X>=Nr. |
595 | | clpfd_check_geq_nr(X,_) :- |
596 | | (nonvar(X) % something like X+Y |
597 | | ; preferences:preference(use_clpfd_solver,false)), |
598 | | !. |
599 | | clpfd_check_geq_nr(X,Nr) :- |
600 | | fd_min(X,Low), number(Low), Low>=Nr. |
601 | | |
602 | | clpfd_size(Var,Size) :- number(Var),!, Size=1. |
603 | | clpfd_size(Var,Size) :- |
604 | | (preferences:preference(use_clpfd_solver,false) |
605 | | -> Size = inf |
606 | | ; fd_size(Var,Size) |
607 | | ). |
608 | | |
609 | | :- assert_must_succeed(clpfd_interface:clpfd_alldifferent([1,2,3])). |
610 | | :- assert_must_succeed(clpfd_interface:all_dif([1,2,3],[])). |
611 | | :- assert_must_fail(clpfd_interface:clpfd_alldifferent([1,2,3,2])). |
612 | | :- assert_must_fail(clpfd_interface:all_dif([1,2,3,2],[])). |
613 | | |
614 | | clpfd_alldifferent([]) :- !. |
615 | | clpfd_alldifferent(L) :- post_constraint(all_different(L),all_dif(L,[])). |
616 | | |
617 | | all_dif([],_). |
618 | | all_dif([H|T],All) :- all_dif_aux(All,H), all_dif(T,[H|All]). |
619 | | |
620 | | all_dif_aux([],_). |
621 | | all_dif_aux([H|T],X) :- dif(H,X), all_dif_aux(T,X). |
622 | | |
623 | | /* |
624 | | clpfd_labeling(Variables) :- |
625 | | (preferences:preference(use_clpfd_solver,false) -> true |
626 | | ; labeling([ffc,enum],Variables)). %ff, enum |
627 | | */ |
628 | | |
629 | | % a version of labeling that performs randomized enumeration if requested |
630 | | % Currently, random enumeration does not support variable selection options |
631 | | % TODO: Maybe we should only randomize enumeration on "large enough" intervals |
632 | | :- use_module(library(random)). |
633 | | :- use_module(extension('random_permutations/random_permutations')). |
634 | | clpfd_randomised_labeling([],ListOfVars) :- |
635 | ? | preferences:get_preference(randomise_enumeration_order,true) |
636 | | -> clpfd_randomised_labeling_aux(ListOfVars) |
637 | ? | ; labeling([],ListOfVars). |
638 | | clpfd_randomised_labeling([Op|Ops],ListOfVars) :- |
639 | | labeling([Op|Ops],ListOfVars). |
640 | | |
641 | | clpfd_randomised_labeling_aux([]). |
642 | | clpfd_randomised_labeling_aux([Var|Vars]) :- |
643 | | clpfd_randomised_in_domain(Var), |
644 | | clpfd_randomised_labeling_aux(Vars). |
645 | | |
646 | | clpfd_randomised_in_domain(Var) :- |
647 | | init_random_permutations, |
648 | | clpfd_domain(Var,Low,Up), |
649 | | ((\+ number(Low) ; \+ number(Up)) |
650 | | -> add_internal_error('Unbounded FD variable: ',clpfd_randomised_in_domain(Var)), |
651 | | %trace, kernel_waitflags:get_fd_priority(Var,_), |
652 | | fail |
653 | | ; true), |
654 | | IntervalLength is Up - Low + 1, |
655 | | get_num_bits(IntervalLength,MaxIdx,NumBits), |
656 | | get_masks(NumBits,LeftMask,RightMask), |
657 | | % the seed relies on the random predicate, not on now/1, thus prob can be made deterministic by setting a central random seed |
658 | | random(TempSeed), |
659 | | Seed is floor(TempSeed * 10000), |
660 | | clpfd_randomised_labeling_aux(Var,0,MaxIdx,Low,Up,Seed,NumBits,LeftMask,RightMask). |
661 | | |
662 | | clpfd_randomised_labeling_aux(N,CurIdx,MaxIdx,From,To,Seed,NumBits,LeftMask,RightMask) :- |
663 | | random_permutation_element(CurIdx,MaxIdx,From,To,Seed,NumBits,LeftMask,RightMask,Drawn,NextIdx), |
664 | | %format('enumerate_int2: Setting variable ~w to ~w~n',[N,Drawn]), |
665 | | ( N=Drawn |
666 | | ; clpfd_randomised_labeling_aux(N,NextIdx,MaxIdx,From,To,Seed,NumBits,LeftMask,RightMask)). |
667 | | |
668 | | |
669 | | %:- use_module(library(lists),[reverse/2]). |
670 | | %clpfd_reverse_labeling(Vars) :- clpfd_reverse_labeling(Vars,[ffc,enum]). |
671 | | %% label a list of variables which was reversed: |
672 | | %clpfd_reverse_labeling(Variables,Opt) :- var(Variables),!, |
673 | | % add_internal_error('Must be list of variables: ',clpfd_reverse_labeling(Variables,Opt)). |
674 | | %clpfd_reverse_labeling(Variables,Options) :- !, |
675 | | % reverse(Variables,RevV), |
676 | | % clpfd_labeling(RevV,Options). |
677 | | |
678 | ? | clpfd_labeling(Variables,Options) :- !, |
679 | ? | on_exception(error(instantiation_error,Err),labeling(Options,Variables), |
680 | | (print(Err),nl, |
681 | | % this can happen when CLP(FD) has a time-out for other constraints |
682 | | add_error(clpfd_interface,'CLP(FD) Variables not set up for ',labeling(Options,Variables)))). % Options = [ffc,enum] |
683 | | |
684 | | |
685 | ? | clpfd_in_domain(Variable) :- !, |
686 | ? | on_exception(error(instantiation_error,Err),indomain(Variable), |
687 | | (print(Err),nl, |
688 | | % this can happen when CLP(FD) has a time-out for other constraints |
689 | | add_error(clpfd_interface,'CLP(FD) Variables not set up for ',indomain(Variable)))). |
690 | | |
691 | | |
692 | | clpfd_degree(Variable,Degree) :- fd_degree(Variable,Degree). % can be called with plain variables and with numbers |
693 | | |
694 | | % a new predicate to obtain the next variable that would be enumerated |
695 | | % (useful if you want to interleave other labeling with CLPFD labeling) |
696 | | |
697 | | :- assert_must_succeed((clpfd:in(X,1..3), clpfd:in(Y,3..5), clpfd:'#>'(X,Z), clpfd_interface:clpfd_get_next_variable_to_label([Y,2,X,3],V),V==X, X=3, Y=3, Z=2)). |
698 | | clpfd_get_next_variable_to_label(Variables,Var) :- |
699 | | clpfd_get_next_variable_to_label(Variables,Var,ffc). |
700 | | |
701 | | % possible options : ff, ffc, step, enum, bisect, median, ... |
702 | | clpfd_get_next_variable_to_label(Variables,Var,Opt) :- Variables \= [], % there is a segmentation fault if we call fd_delete with empty list |
703 | | % this also causes segmentation fault: |
704 | | % X in 1..2 , Y in 3..5, clpfd_get_next_variable_to_label([2,3,Y,2,X],Var,ffc). rlwrap: warning: sicstus crashed, killed by SIGSEGV. |
705 | | clpfd:'$fd_delete'(Variables,Var,Opt). |
706 | | |
707 | | /* ------------ |
708 | | % a version of the above just for ffc, re-implemented by hand so as not to be dependent on internal predicates of clpfd |
709 | | % also: it returns the remaining variables (filters out non-variables) |
710 | | % but it can be much slower than clpfd:$fd_delete (e.g., NQueens50) |
711 | | clpfd_get_next_variable_to_label_ffc([H|T],Var,RestV) :- |
712 | | (get_fd_info(H,HI) -> get_next_aux(T,H,HI,Var,RestV) |
713 | | %,((clpfd_get_next_variable_to_label([H|T],VV,ffc), VV\==Var) -> print(different_choice(Var,VV)),nl , tools_printing:print_vars([H|T]) ; true) |
714 | | ; clpfd_get_next_variable_to_label_ffc(T,Var,RestV) |
715 | | ). |
716 | | % , tools_printing:print_vars([H|T]),print(sel(Var,RestV)),nl. |
717 | | |
718 | | get_next_aux([],Var,_,Var,[]). |
719 | | get_next_aux([H|T],VarSoFar,VInfo,Var,Rest) :- |
720 | | (get_fd_info(H,HI) |
721 | | -> (HI @< VInfo -> Rest = [VarSoFar|TRest], get_next_aux(T,H,HI,Var,TRest) |
722 | | ; Rest = [H|TRest], get_next_aux(T,VarSoFar,VInfo,Var,TRest) |
723 | | ) |
724 | | ; get_next_aux(T,VarSoFar,VInfo,Var,Rest)). |
725 | | |
726 | | get_fd_info(Var,(Sz,NDg)) :- fd_size(Var,Sz), Sz \= 1, % note: we have 2 @<sup |
727 | | % kernel_waitflags:size_of_attached_goals(Var,Dg), |
728 | | fd_degree(Var,Dg), |
729 | | NDg is -Dg. % negate to give higher priority to goals with high degree |
730 | | ----- */ |
731 | | |
732 | | % would could also use size of attached frozen goals as a tiebreaker : frozen_degree(Var,FDg), NFDg is -FDg. |
733 | | % maybe we should compute this only on demand |
734 | | %frozen_degree(Var,Dg) :- var(Var), !,frozen(Var,Goal), size_of_goal(Goal,Dg). |
735 | | %frozen_degree(_,0). |
736 | | %size_of_goal((A,B),S) :- !, size_of_goal(A,SA), size_of_goal(B,SB), S is SA+SB. |
737 | | %size_of_goal(_,1). |
738 | | |
739 | | |
740 | | :- meta_predicate catch_clpfd_overflow_call1(0). |
741 | | :- meta_predicate catch_clpfd_overflow_call2(0,0). |
742 | ? | catch_clpfd_overflow_call1(Call) :- catch_clpfd_overflow_call2(Call,fail). |
743 | | catch_clpfd_overflow_call2(Call,Handler) :- |
744 | ? | on_exception(error(representation_error(ErrMsg),E), |
745 | | Call, |
746 | | handle_representation_error(ErrMsg,E,Handler)). |
747 | | |
748 | | handle_representation_error(ErrMsg,_E,Call) :- |
749 | | (is_clpfd_overflow_representation_error(ErrMsg) |
750 | | -> clpfd_overflow_error_message, Call |
751 | | ; throw(representation_error(ErrMsg))). |
752 | | |
753 | | is_clpfd_overflow_representation_error('CLPFD integer overflow'). |
754 | | is_clpfd_overflow_representation_error('max_clpfd_integer'). |
755 | | is_clpfd_overflow_representation_error('min_clpfd_integer'). |
756 | | |
757 | | |
758 | | |
759 | | /* |
760 | | Testing for how large the integers can be, 32-bit system: |
761 | | | ?- X is integer(2**30), Y#=X+1. |
762 | | ! Representation error in argument 2 of user:'t=u+c'/3 |
763 | | ! CLPFD integer overflow |
764 | | ! goal: 't=u+c'(_102,1073741824,1) |
765 | | | ?- X is integer(2**30), Y#=X. |
766 | | X = 1073741824, |
767 | | Y = 1073741824 ? |
768 | | 64-bit system: |
769 | | | ?- X is integer(2**60), Y#=X. |
770 | | ! Representation error in argument 2 of user:(#=)/2 |
771 | | ! CLPFD integer overflow |
772 | | ! goal: _413#=1152921504606846976 |
773 | | |
774 | | | ?- X is integer(2**60)-1, Y#=X. |
775 | | X = 1152921504606846975, |
776 | | Y = 1152921504606846975 ? |
777 | | yes |
778 | | */ |
779 | | |
780 | | |