| 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(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_nr_eq/2, |
| 12 | | clpfd_geq2/3, |
| 13 | | clpfd_geq/3, clpfd_gt/3, clpfd_leq/3, clpfd_lt/3, |
| 14 | | clpfd_leq_expr/2, clpfd_lt_expr/2, |
| 15 | | clpfd_inrange/3, clpfd_inrange/4, |
| 16 | | clpfd_not_inrange/3, clpfd_not_in_non_empty_range/3, |
| 17 | | clpfd_sum/2, |
| 18 | | clpfd_inlist/2, clpfd_not_inlist/2, |
| 19 | | clpfd_reify_inlist/4, force_clpfd_inlist/2, |
| 20 | | clpfd_minimum/2, clpfd_maximum/2, |
| 21 | | clpfd_if_then_else/4, |
| 22 | | clpfd_domain/3, clpfd_size/2, |
| 23 | | clpfd_in_domain/3, |
| 24 | | clpfd_randomised_enum/3, |
| 25 | | clpfd_some_element_in_domain/2, |
| 26 | | clpfd_max_bounded/1, |
| 27 | | clpfd_can_match/2, |
| 28 | | clpfd_check_geq_nr/2, |
| 29 | | clpfd_alldifferent/1, |
| 30 | | clpfd_degree/2, |
| 31 | | clpfd_in_domain/1, clpfd_labeling/2, |
| 32 | | clpfd_can_intersect/2, |
| 33 | | clpfd_get_next_variable_to_label/2, |
| 34 | | %clpfd_get_next_variable_to_label_ffc/3, |
| 35 | | is_64_bit_system/0, clpfd_overflow_error_message/0, clpfd_overflow_warning_message/0, |
| 36 | | integer_too_large_for_clpfd/1, integer_too_small_for_clpfd/1, |
| 37 | | catch_clpfd_overflow_call1/1, catch_clpfd_overflow_call2/2, catch_clpfd_overflow_call3/3, |
| 38 | | catch_and_ignore_clpfd_overflow/2, |
| 39 | | computable_arith_expression/1, |
| 40 | | clpfd_observe/2, clpfd_observe_state/1, |
| 41 | | clpfd_gcd/3, clpfd_lcm/3, clpfd_abs/2, clpfd_sign/2]). |
| 42 | | |
| 43 | | :- meta_predicate try_post_constraint(0). |
| 44 | | :- meta_predicate post_constraint(0,0). |
| 45 | | :- meta_predicate post_constraint2(0,*). |
| 46 | | :- meta_predicate time_out_constraint(0,0). |
| 47 | | %:- meta_predicate force_post_constraint(0). |
| 48 | | %:- meta_predicate force_post_constraint(0,0). |
| 49 | | :- meta_predicate catch_clpfd_overflow_call1(0). |
| 50 | | :- meta_predicate catch_clpfd_overflow_call2(0,0). |
| 51 | | :- meta_predicate catch_clpfd_overflow_call3(0,*,0). |
| 52 | | :- meta_predicate catch_and_ignore_clpfd_overflow(-,0). |
| 53 | | :- meta_predicate handle_representation_error(*,*,0). |
| 54 | | |
| 55 | | :- load_files(library(system), [when(compile_time), imports([environ/2])]). |
| 56 | | :- use_module(library(clpfd)). |
| 57 | | :- use_module(chrsrc(chr_integer_inequality),[chr_leq/2, chr_lt/2, chr_eq/2, chr_eq/3, chr_neq/2]). |
| 58 | | |
| 59 | | |
| 60 | | |
| 61 | | %:- use_module(typechecker). |
| 62 | | :- use_module(tools). |
| 63 | | :- use_module(preferences). |
| 64 | | :- use_module(library(timeout)). |
| 65 | | :- use_module(error_manager). |
| 66 | | :- use_module(self_check). |
| 67 | | |
| 68 | | :- use_module(module_information,[module_info/2]). |
| 69 | | :- module_info(group,kernel). |
| 70 | | :- module_info(description,'Provide interface to CLP(FD), with Prolog fallback in case of errors or when CLP(FD) turned off.'). |
| 71 | | |
| 72 | | % an equality which tries to avoid CLP(FD) overflows |
| 73 | | clpfd_nr_eq(Nr,FDVar) :- integer(Nr), var(FDVar),!, |
| 74 | | clpfd_domain(FDVar,Low,Up), |
| 75 | | (number(Low), Low>Nr -> fail % fail without unification; prevent overflows |
| 76 | | ; number(Up), Up<Nr -> fail % ditto |
| 77 | | ; FDVar=Nr). |
| 78 | | clpfd_nr_eq(Nr,E2) :- Nr=E2. |
| 79 | | |
| 80 | | % Note: clpfd_eq is sometimes called with arithmetic expressions (treatment of plus/minus/...) |
| 81 | | % does not do the computable_arith_expression check |
| 82 | | clpfd_eq(E1,E2) :- simple(E1), simple(E2), % was (before 29.8.2014): number(E1),number(E2), |
| 83 | | !, |
| 84 | | E1=E2. |
| 85 | | clpfd_eq(E1,E2) :- |
| 86 | | (preferences:preference(use_chr_solver,true) -> chr_eq(E1,E2) ; true), |
| 87 | ? | try_post_constraint( E1 #= E2 ). |
| 88 | | |
| 89 | | clpfd_neq(E1,E2) :- number(E1),!, |
| 90 | | (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 |
| 91 | | clpfd_neq(E1,E2) :- number(E2),!, % no need to post dif constraint in clpfd mode |
| 92 | | post_constraint( E1 #\= E2, dif(E1,E2)). |
| 93 | | clpfd_neq(E1,E2) :- |
| 94 | | % Note: | ?- X #\= Y, Y #=X. --> does not fail; hence we also execute dif(E1,E2) in CLPFD mode ! |
| 95 | | % We might be able to skip calling dif, if we enable the CHR solver by default |
| 96 | | % Let's try it: only set up dif if CHR is disabled! |
| 97 | | (preferences:preference(use_chr_solver,true) -> chr_neq(E1,E2) ; dif(E1,E2)), |
| 98 | | try_post_constraint(E1 #\= E2). |
| 99 | | % clpfd_domain(E1,D1,D2), clpfd_domain(E2,B1,B2), print(dom(D1:D2,B1:B2)),nl. |
| 100 | | |
| 101 | | % set up a dif co-routine only if necessary; assumption: E1,E2 are either number or variable |
| 102 | | dif_when_necesssary(E1,E2) :- %print(dif_when_necesssary(E1,E2)),nl, |
| 103 | | var(E1),var(E2),!, dif(E1,E2). |
| 104 | | dif_when_necesssary(_,_). |
| 105 | | |
| 106 | | % this will force Y to be nonzero |
| 107 | | clpfd_eq_div(E1,X,Y) :- post_constraint( E1 #= X//Y, prolog_eq(E1,X//Y)), % truncated division -1 // 4 = 0 |
| 108 | | propagate_div_eq_eq(E1,X,Y). |
| 109 | | clpfd_eq_fdiv(E1,X,Y) :- post_constraint( E1 #= X div Y, prolog_eq(E1,X div Y)). % floored division -1 div 4 = -1 |
| 110 | | |
| 111 | | % certain == propagation rules not done by CLPFD |
| 112 | | propagate_div_eq_eq(E1,X,Y) :- |
| 113 | | (var(E1),X==Y |
| 114 | | -> %print(setting1(E1,X,Y)),nl, |
| 115 | | E1=1 % X/X can only be 1 or undefined when X=0 |
| 116 | | ; true). |
| 117 | | % we could check if E1==X --> Y=1 if X /= 0 |
| 118 | | |
| 119 | | % this waits until we are sure Y is not zero |
| 120 | | % E1 = X / Y |
| 121 | | clpfd_eq_guarded_div(E1,X,Y) :- |
| 122 | | post_constraint3((E1 #= X//YY #/\ MY in 0..1 #/\ Y #= MY*YY), % Y can either be 0 or equal to YY |
| 123 | | prolog_eq(E1,X//Y),Posted), |
| 124 | | % Note: E1 can be at most abs(X); if the range of E1 is already beyond that the above will fail |
| 125 | | % and not detect WD errors ! |
| 126 | | % this is better than the previous solution: |
| 127 | | % post_constraint(Y#\=0 #=> YY#=Y, prolog_eq(Y,YY)). |
| 128 | | % however, x:44..77 & E : 1..5555 & E = x/y & y:0..4 only restricts E to 1..77, not 11..77 |
| 129 | | % what is still missing is propagating upper bounds of Y to YY |
| 130 | | % Y in 0..4, M in 0..1, YY in 1..77, Y #=M*YY |
| 131 | | % with this propagation below, we at least obtain propagation for x:44..77 & E : 1..5555 & y:0..4 & E = x/y |
| 132 | | % what we need is continously propagating the bounds from Y to YY (but not vice versa for 0) |
| 133 | | % maybe we can achieve this using indexicals ?? |
| 134 | | (Posted==true |
| 135 | | -> fd_max(Y,YUp), (number(YUp) -> YY #=< YUp ; true), |
| 136 | | fd_min(Y,YLow), (number(YLow) -> YY #>= YLow ; true), |
| 137 | | propagate_div_eq_eq(E1,X,Y) |
| 138 | | ; true). |
| 139 | | |
| 140 | | /* |
| 141 | | Note: X #= Y//0 fails in CLPFD. |
| 142 | | Here is an example: |
| 143 | | | ?- X = 1, Y #>= 0, E1 #= X//YY #/\ MY in 0..1 #/\ Y #= MY*YY. |
| 144 | | X = 1, |
| 145 | | Y in 0..sup, |
| 146 | | YY in(inf.. -1)\/(1..sup), |
| 147 | | E1 in-1..1, |
| 148 | | MY in 0..1 ? |
| 149 | | As you can see, we infer that the result is -1..1; this can prevent finding WD errors |
| 150 | | */ |
| 151 | | |
| 152 | | % now a similar solution for floored division |
| 153 | | clpfd_eq_guarded_fdiv(E1,X,Y) :- |
| 154 | | post_constraint3((E1 #= X div YY #/\ MY in 0..1 #/\ Y #= MY*YY), prolog_eq(E1,X div Y),_Posted). |
| 155 | | % TO DO: add propagation like above or using indexicals |
| 156 | | |
| 157 | | % do we need to instantiate YY if Y becomes 0 ?? |
| 158 | | %:- block copy_divisor(-,?). |
| 159 | | %copy_divisor(Y,Y). |
| 160 | | |
| 161 | | |
| 162 | | |
| 163 | | % we check whether we have simple expressions of the form X = Y OP Z |
| 164 | | % in this case it is better to call ProB's implementations, as they have stronger propagation |
| 165 | | % (e.g., 0 = X-Y => X=Y or 1 = X-Y => X/=Y) |
| 166 | | clpfd_eq_expr_optimized(CLPFD_Expr1,CLPFD_Expr2) :- |
| 167 | ? | (simple(CLPFD_Expr1) -> call_eq_expr1(CLPFD_Expr2,CLPFD_Expr1) |
| 168 | ? | ; simple(CLPFD_Expr2) -> call_eq_expr2(CLPFD_Expr1,CLPFD_Expr2) |
| 169 | | ; clpfd_eq_expr_nonsimple(CLPFD_Expr1,CLPFD_Expr2)). |
| 170 | | |
| 171 | | :- use_module(kernel_dif). |
| 172 | | call_eq_expr1(CLPFD_Expr2,CLPFD_Expr1) :- |
| 173 | | (simple(CLPFD_Expr2) -> %print(unify(CLPFD_Expr2,CLPFD_Expr1)),nl, translate:print_clpfd_variable(CLPFD_Expr2),nl, translate:print_clpfd_variable(CLPFD_Expr1),nl, |
| 174 | | % first check if we have a pending dif; unification could trigger CLPFD enumeration |
| 175 | | ((var(CLPFD_Expr1),kernel_dif:frozen_dif(CLPFD_Expr2,CLPFD_Expr1)) -> /* print(dif),nl,*/ fail ; true), |
| 176 | | CLPFD_Expr2=CLPFD_Expr1 |
| 177 | | ; call_eq_expr2(CLPFD_Expr2,CLPFD_Expr1)). |
| 178 | | |
| 179 | | % first arg not simple, second arg simple |
| 180 | | call_eq_expr2(CLPFD_Expr2,CLPFD_Expr1) :- CLPFD_Expr2 = -(A,B),simple(A),simple(B),!, |
| 181 | | %kernel_objects:int_minus(int(A),int(B),int(CLPFD_Expr1)). % has special propagation rules |
| 182 | | (number(A),number(B) -> CLPFD_Expr1 is CLPFD_Expr2 |
| 183 | | ; clpfd_eq_expr_nonsimple(CLPFD_Expr2,CLPFD_Expr1), |
| 184 | | propagate_zero(A,B,CLPFD_Expr1)). |
| 185 | | call_eq_expr2(CLPFD_Expr2,CLPFD_Expr1) :- CLPFD_Expr2 = +(A,B),simple(A),simple(B),!, |
| 186 | | (number(A),number(B) -> CLPFD_Expr1 is CLPFD_Expr2 |
| 187 | ? | ; clpfd_eq_expr_nonsimple(CLPFD_Expr1,CLPFD_Expr2), |
| 188 | | propagate_zero(CLPFD_Expr1,A,B)). |
| 189 | | %kernel_objects:int_plus(int(A),int(B),int(CLPFD_Expr1)). % has special propagation rules |
| 190 | | call_eq_expr2(CLPFD_Expr2,CLPFD_Expr1) :- |
| 191 | | (computable_arith_expression(CLPFD_Expr2) -> CLPFD_Expr1 is CLPFD_Expr2 |
| 192 | | ; clpfd_eq_expr_nonsimple(CLPFD_Expr1,CLPFD_Expr2)). |
| 193 | | |
| 194 | | % a propagation rule not done by CLP(FD) |
| 195 | | :- block propagate_zero(-,-,-). |
| 196 | | propagate_zero(Sum,A,B) :- % print(prop_zero(Sum,A,B)),nl, |
| 197 | | (A==0 -> Sum=B ; B==0 -> Sum=A ; true). |
| 198 | | |
| 199 | | % eq_expr takes expressions as arguments |
| 200 | | clpfd_eq_expr(E1,E2) :- simple(E1),!, |
| 201 | | (simple(E2) -> E1=E2 |
| 202 | | ; computable_arith_expression(E2) -> E1 is E2 |
| 203 | | ; clpfd_eq_expr_nonsimple(E1,E2)). |
| 204 | | clpfd_eq_expr(E1,E2) :- simple(E2),computable_arith_expression(E1),!, E2 is E1. |
| 205 | | clpfd_eq_expr(E1,E2) :- clpfd_eq_expr_nonsimple(E1,E2). |
| 206 | | |
| 207 | | clpfd_eq_expr_nonsimple(E1,E2) :- |
| 208 | | (preferences:preference(use_chr_solver,true) -> chr_eq(E1,E2) ; true), |
| 209 | ? | post_constraint( E1 #= E2, prolog_eq(E1,E2)). |
| 210 | | %% post_constraint( E1 #= E2). % Used instead of line below because of serious bug in SICStus 4.1.3 |
| 211 | | % BUG: X*X #= 100, X #= 9. succeeds ! |
| 212 | | |
| 213 | | :- assert_must_succeed((clpfd_interface:prolog_eq(X,2+1),X==3)). |
| 214 | | :- assert_must_succeed((clpfd_interface:prolog_eq(X,2+Z),Z=3,X==5)). |
| 215 | | :- assert_must_succeed((clpfd_interface:prolog_eq(X,2+Z),X=5,Z=3)). |
| 216 | | :- assert_must_succeed((clpfd_interface:prolog_eq(4-1,X),X==3)). |
| 217 | | :- assert_must_succeed((clpfd_interface:prolog_eq(5-2,(2*2)-Y),Y=1)). |
| 218 | | :- assert_must_succeed((clpfd_interface:prolog_eq(5-X,(2*2)-Y),Y=1,X=2)). |
| 219 | | :- assert_must_fail((clpfd_interface:prolog_eq(5-2,(2*2)-Y),Y=0)). |
| 220 | | % used as Prolog backup when posting constraints, only right-hand side can be an arithmetic term |
| 221 | | prolog_eq(A,B) :- simple(A),!, when(ground(B), A is B). |
| 222 | | prolog_eq(A,B) :- simple(B),!, when(ground(A), B is A). |
| 223 | | prolog_eq(A,B) :- when((ground(A),ground(B)),=:=(A,B)). |
| 224 | | |
| 225 | | computable_arith_expression(X) :- number(X),!. |
| 226 | | computable_arith_expression(X) :- var(X),!,fail. |
| 227 | | computable_arith_expression(-(X)) :- computable_arith_expression(X). |
| 228 | | computable_arith_expression(abs(X)) :- computable_arith_expression(X). |
| 229 | | computable_arith_expression(+(X,Y)) :- computable_arith_expression(X),computable_arith_expression(Y). |
| 230 | | computable_arith_expression(-(X,Y)) :- computable_arith_expression(X),computable_arith_expression(Y). |
| 231 | | computable_arith_expression(*(X,Y)) :- computable_arith_expression(X),computable_arith_expression(Y). |
| 232 | | |
| 233 | | clpfd_neq_expr(E1,E2) :- number(E1),number(E2),!,E1\=E2. |
| 234 | | clpfd_neq_expr(E1,E2) :- %print(neq_expr(E1,E2)),nl, |
| 235 | | var(E1), var(E2), !, |
| 236 | | (preferences:preference(use_chr_solver,true) -> chr_neq(E1,E2) ; dif(E1,E2)), |
| 237 | | try_post_constraint( E1 #\= E2 ). % does not matter if posted |
| 238 | | % Note: | ?- X #\= Y, Y #=X. --> does not fail; hence we also execute dif(E1,E2) in CLPFD mode ! |
| 239 | | clpfd_neq_expr(E1,E2) :- % print(neq_expr(E1,E2)),nl, |
| 240 | | post_constraint3( E1 #\= E2, prolog_neq(E1,E2), Posted), |
| 241 | | (Posted=true -> my_dif(E1,E2) ; true). |
| 242 | | |
| 243 | | :- assert_must_succeed((clpfd_interface:prolog_neq(X,2+1),X=2)). |
| 244 | | :- assert_must_succeed((clpfd_interface:prolog_neq(X,2+Z),X=2,Z=1)). |
| 245 | | :- assert_must_succeed((clpfd_interface:prolog_neq(X,2+Z),Z=1,X=2)). |
| 246 | | :- assert_must_fail((clpfd_interface:prolog_neq(X,2+1),X=3)). |
| 247 | | :- assert_must_succeed((clpfd_interface:prolog_neq(3,X),X=2)). |
| 248 | | :- assert_must_succeed((clpfd_interface:prolog_neq(5-2,(2*2)-Y),Y=2)). |
| 249 | | % used as Prolog backup when posting constraints, |
| 250 | | prolog_neq(A,B) :- simple(A),simple(B),!, dif(A,B). |
| 251 | | prolog_neq(A,B) :- A \== B, when((ground(A),ground(B)),=\=(A,B)). |
| 252 | | |
| 253 | | my_dif(A,B) :- (simple(A);simple(B)),!. % do we need to setup things like dif(X,0) ? |
| 254 | | my_dif(A,B) :- comm_assoc_dif(A,B). |
| 255 | | |
| 256 | | % note: here we do not block, we use \== now rather than waiting on variables to be bound |
| 257 | | comm_assoc_dif(A,B) :- var(A),!, A \== B. |
| 258 | | comm_assoc_dif(A,B) :- var(B),!, A \== B. |
| 259 | | comm_assoc_dif(*(A1,A2),*(B1,B2)) :- !, |
| 260 | | extract_factors2(A1,A2,FA,[]), sort(FA,SFA), |
| 261 | | extract_factors2(B1,B2,FB,[]), sort(FB,SFB), |
| 262 | | %print(comm_assoc_dif_mul(SFA,SFB)),nl, |
| 263 | | SFA \== SFB. |
| 264 | | comm_assoc_dif(+(A1,A2),+(B1,B2)) :- !, |
| 265 | | extract_terms2(A1,A2,FA,[]), sort(FA,SFA), |
| 266 | | extract_terms2(B1,B2,FB,[]), sort(FB,SFB), |
| 267 | | %print(comm_assoc_dif_sum(SFA,SFB)),nl, |
| 268 | | SFA \== SFB. |
| 269 | | comm_assoc_dif(-(A1),-(B1)) :- !, comm_assoc_dif(A1,B1). |
| 270 | | comm_assoc_dif(-(A1,A2),-(B1,B2)) :- !, |
| 271 | | (comm_assoc_dif(A1,B1) -> true ; comm_assoc_dif(A2,B2)). |
| 272 | | % note : exponentiation and division are not handed directly to CLPFD at the moment, so no need to check here: |
| 273 | | %comm_assoc_dif(**(A1,A2),**(B1,B2)) :- !, (comm_assoc_dif(A1,B1) -> true ; comm_assoc_dif(A2,B2)). |
| 274 | | comm_assoc_dif(A,B) :- A \== B. % ,((nonvar(A),nonvar(B)) -> nl,print(cadif(A,B)),nl ; true). |
| 275 | | |
| 276 | | extract_factors2(A,B) --> extract_factors(A), extract_factors(B). |
| 277 | | extract_factors(A) --> {var(A)},!, [A]. |
| 278 | | extract_factors(*(A,B)) --> !, extract_factors(A), extract_factors(B). |
| 279 | | extract_factors(+(A,B)) --> !, [sum(ST)], {extract_terms2(A,B,T,[]),sort(T,ST)}. |
| 280 | | extract_factors(A) --> [A]. |
| 281 | | |
| 282 | | extract_terms2(A,B) --> extract_terms(A), extract_terms(B). |
| 283 | | extract_terms(A) --> {var(A)},!, [A]. |
| 284 | | extract_terms(+(A,B)) --> !, extract_terms(A), extract_terms(B). |
| 285 | | extract_terms(*(A,B)) --> !, [mul(ST)], {extract_factors2(A,B,T,[]),sort(T,ST)}. |
| 286 | | extract_terms(A) --> [A]. |
| 287 | | |
| 288 | | |
| 289 | | :- assert_must_succeed((clpfd_interface:clpfd_leq_expr(3,X+1),X=3)). |
| 290 | | :- assert_must_fail((clpfd_interface:clpfd_leq_expr(2,X-1),X=2)). |
| 291 | | clpfd_leq_expr(E1,E2) :- number(E1),number(E2), !, E1 =< E2. |
| 292 | | clpfd_leq_expr(E1,E2) :- useless_leq_constraint(E1,E2),!. |
| 293 | | clpfd_leq_expr(E1,E2) :- |
| 294 | | (preferences:preference(use_chr_solver,true) -> chr_leq(E1,E2) ; true), |
| 295 | | post_constraint( E1 #=< E2, prolog_leq(E1,E2)). |
| 296 | | prolog_leq(A,B) :- when((ground(A),ground(B)),=<(A,B)). |
| 297 | | |
| 298 | | :- assert_must_succeed((clpfd_interface:clpfd_lt_expr(3,X+1),X=3)). |
| 299 | | :- assert_must_fail((clpfd_interface:clpfd_lt_expr(2,X-1),X=3)). |
| 300 | | clpfd_lt_expr(E1,E2) :- number(E1),number(E2), !, E1 < E2. |
| 301 | | clpfd_lt_expr(E1,E2) :- useless_lt_constraint(E1,E2),!. |
| 302 | | clpfd_lt_expr(E1,E2) :- |
| 303 | | (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 |
| 304 | | post_constraint( E1 #< E2, prolog_lt(E1,E2)). |
| 305 | | prolog_lt(A,B) :- when((ground(A),ground(B)),<(A,B)). |
| 306 | | |
| 307 | | /* TO DO: do custom equalities div, plus, ... |
| 308 | | mydiv(X, Y, Z) :- |
| 309 | | (Y#=0 #\/ (Y#\=0 #/\ X/Y #= Z)). */ |
| 310 | | |
| 311 | | % a version of geq which avoids posting constraints if possible |
| 312 | | clpfd_geq2(E1,E2,Posted) :- number(E1),number(E2),!, E1 >= E2, Posted=true. |
| 313 | | clpfd_geq2(E1,E2,Posted) :- |
| 314 | | (preferences:preference(use_chr_solver,true) -> chr_leq(E2,E1) ; true), |
| 315 | | %E1 #>= E2, Posted=true. |
| 316 | | post_constraint2( E1 #>= E2 , Posted). |
| 317 | | |
| 318 | | clpfd_geq(E1,E2,Posted) :- clpfd_leq(E2,E1,Posted). |
| 319 | | clpfd_gt(E1,E2,Posted) :- clpfd_lt(E2,E1,Posted). |
| 320 | | |
| 321 | | clpfd_leq(E1,E2,Posted) :- |
| 322 | | useless_leq_constraint(E1,E2), |
| 323 | | !, Posted=true. % we have a useless constraint which is already known; avoid overhead of catch and time_out |
| 324 | | clpfd_leq(E1,E2,Posted) :- |
| 325 | | (preferences:preference(use_chr_solver,true) -> chr_leq(E1,E2) ; true), |
| 326 | | %E1 #=< E2, Posted=true. |
| 327 | ? | post_constraint2( E1 #=< E2 , Posted). |
| 328 | | |
| 329 | | useless_leq_constraint(E1,E2) :- number(E1), !, var(E2), fd_min(E2,Low), number(Low),E1 =< Low. |
| 330 | | useless_leq_constraint(E1,E2) :- number(E2), var(E1), fd_max(E1,Up), number(Up),Up =< E2. |
| 331 | | |
| 332 | | |
| 333 | | clpfd_lt(E1,E2,Posted) :- |
| 334 | | useless_lt_constraint(E1,E2), |
| 335 | | !, Posted=true. % we have a useless constraint which is already known; avoid overhead of catch and time_out |
| 336 | | clpfd_lt(E1,E2,Posted) :- |
| 337 | | (preferences:preference(use_chr_solver,true) -> chr_lt(E1,E2) ; dif_when_necesssary(E1,E2)), |
| 338 | ? | post_constraint2( E1 #< E2 , Posted). |
| 339 | | |
| 340 | | useless_lt_constraint(E1,E2) :- number(E1), !, var(E2), fd_min(E2,Low), number(Low),E1 < Low. |
| 341 | | useless_lt_constraint(E1,E2) :- number(E2), var(E1), fd_max(E1,Up), number(Up),Up < E2. |
| 342 | | |
| 343 | | clpfd_inrange(X,Low,Up) :- clpfd_inrange(X,Low,Up,_Posted). |
| 344 | | clpfd_inrange(X,Low,Up,Posted) :- |
| 345 | | (Low==Up -> X=Low, Posted=true |
| 346 | | ; (number(Low),number(Up) |
| 347 | | -> Low =< Up, % otherwise interval empty |
| 348 | | (number(X) -> X >= Low, X =< Up, Posted=true %, Low =<Up |
| 349 | ? | ; post_constraint2(X in Low..Up, Posted) |
| 350 | | % Note: X in 1..Y, Y in 0..3. fails! Range needs to be constant |
| 351 | | % post_chr_inrange(X,Low,Up) % not useful, as CLPFD will be propagating bounds anyway? |
| 352 | | % if one performs CHR propgation before CLPFD, test 2133 fails for |
| 353 | | % xx:0..3 & not(xx>xx-1 & card(xx..xx) = 1) loops |
| 354 | | ) |
| 355 | | ; post_chr_inrange(X,Low,Up), |
| 356 | | post_constraint2( (X #>=Low #/\ X #=< Up),Posted ) % #/\ Low #=< Up should be implied |
| 357 | | %,translate:print_clpfd_variables([X,Low,Up]),nl |
| 358 | | ) |
| 359 | | ). |
| 360 | | |
| 361 | | post_chr_inrange(X,Low,Up) :- |
| 362 | | (preferences:preference(use_chr_solver,true) |
| 363 | | -> chr_leq(Low,X), |
| 364 | | chr_leq(X,Up) |
| 365 | | ; true). |
| 366 | | |
| 367 | | % assumes Low =< Up ! |
| 368 | | clpfd_not_in_non_empty_range(X,Low,Up) :- |
| 369 | ? | post_constraint((X #<Low #\/ X #> Up),outside_range_blocking(X,Low,Up)). |
| 370 | | |
| 371 | | :- block outside_range_blocking(-,?,?). |
| 372 | | outside_range_blocking(X,Low,Up) :- (X<Low ; X>Up). |
| 373 | | |
| 374 | | % assumes Low =< Up ! |
| 375 | | clpfd_not_inrange(X,Low,Up) :- preferences:preference(use_clpfd_solver,false), |
| 376 | | !, % avoid building up constraint below |
| 377 | | not_in_range_blocking(X,Low,Up). |
| 378 | | clpfd_not_inrange(X,Low,Up) :- |
| 379 | | % special treatment due to limitation of CLP(FD) disjunct: | ?- X #>10 #\/ X #>9. ---> X in inf..sup |
| 380 | | post_constraint2(XLow #<=> (X #<Low), Posted1), Posted1==true, |
| 381 | | post_constraint2(UpX #<=> (Up #< X), Posted2), Posted2==true, |
| 382 | | post_constraint2(UpLow #<=> (Up #< Low), Posted3), Posted3==true, |
| 383 | | !, |
| 384 | | clpfd_not_inrange_prop(XLow,UpX,UpLow,X,Low,Up). |
| 385 | | clpfd_not_inrange(X,Low,Up) :- |
| 386 | | force_post_constraint((X #<Low #\/ X #> Up #\/ Up #< Low),not_in_range_blocking(X,Low,Up)). |
| 387 | | |
| 388 | | :- block clpfd_not_inrange_prop(-,-,-,?,?,?). |
| 389 | | clpfd_not_inrange_prop(XLow,UpX,UpLow,_X,_Low,_Up) :- %print(unblock(XLow,UpX,UpLow,X,Low,Up)),nl, |
| 390 | | (XLow==1 ; UpX==1 ; UpLow==1),!. |
| 391 | | clpfd_not_inrange_prop(XLow,UpX,UpLow,X,Low,Up) :- XLow==0,!, |
| 392 | | post_constraint2(Up #< max(X,Low),_), disjunct_reify(UpX,UpLow). |
| 393 | | clpfd_not_inrange_prop(XLow,UpX,UpLow,X,Low,Up) :- UpX==0,!, |
| 394 | | post_constraint2(Low #> min(X,Up),_), disjunct_reify(XLow,UpLow). |
| 395 | | clpfd_not_inrange_prop(XLow,UpX,_UpLow,_X,_Low,_Up) :- % UpLow==0, |
| 396 | | % Anything more we can infer here ?? |
| 397 | | disjunct_reify(XLow,UpX). |
| 398 | | |
| 399 | | :- block disjunct_reify(-,-). |
| 400 | | disjunct_reify(UpX,UpLow) :- (UpX==1 ; UpLow==1),!. |
| 401 | | disjunct_reify(UpX,UpLow) :- (UpX==0 -> UpLow=1 ; UpX=1). |
| 402 | | |
| 403 | | |
| 404 | | :- block not_in_range_blocking(-,?,?),not_in_range_blocking(?,-,?). |
| 405 | | not_in_range_blocking(X,Y,Z) :- |
| 406 | | (X<Y -> true ; not_in_range_blocking2(X,Y,Z)). |
| 407 | | :- block not_in_range_blocking2(?,?,-). |
| 408 | | not_in_range_blocking2(X,Y,Z) :- (Z<Y -> true ; Z<X). |
| 409 | | |
| 410 | | clpfd_sum(List,Sum) :- |
| 411 | | clpfd_sum(List,Sum,Posted), |
| 412 | | (Posted==true -> true |
| 413 | | ; sum_list(List,int(0),int(Sum))). |
| 414 | | clpfd_sum(List,Sum,Posted) :- post_constraint2( sum(List,'#=',Sum),Posted). |
| 415 | | |
| 416 | | :- use_module(kernel_objects,[int_plus/3]). |
| 417 | | % non-CLPFD backup, when posting fails or CLPFD turned off |
| 418 | | :- block sum_list(-,?,?). |
| 419 | | sum_list([],Acc,Acc). |
| 420 | | sum_list([H|T],Acc,Res) :- int_plus(int(H),Acc,NewAcc), sum_list(T,NewAcc,Res). |
| 421 | | |
| 422 | | |
| 423 | | % assert that a variable is one of the numbers in the FDList; FDList must be numbers, cannot be unbound FD variable |
| 424 | | % can fail due to overflows; one should not rely on it for soundness, see test 1353 |
| 425 | | clpfd_inlist(El,[X]) :- !, El=X. |
| 426 | | clpfd_inlist(El,FDList) :- |
| 427 | ? | try_post_constraint( (list_to_fdset(FDList,FDSET), El in_set FDSET)). |
| 428 | | |
| 429 | | % a version that always posts constraints independent of CLPFD preference |
| 430 | | force_clpfd_inlist(El,[X]) :- !, El=X. |
| 431 | | force_clpfd_inlist(El,FDList) :- |
| 432 | | list_to_fdset(FDList,FDSET), El in_set FDSET. |
| 433 | | |
| 434 | | % can fail due to overflows; one should not rely on it for soundness |
| 435 | | clpfd_not_inlist(El,FDList) :- |
| 436 | ? | try_post_constraint( (list_to_fdset(FDList,FDSET), fdset_complement(FDSET,C),El in_set C)). |
| 437 | | |
| 438 | | % library(clpfd) on e. g. SWI does not support maximum/2 and minimum/2 |
| 439 | | |
| 440 | | :- if(\+ predicate_property(maximum(_,_), _)). |
| 441 | | clpfd_maximum(_El,_List). % the current usage in maximum_of_set only uses this as additional constraint |
| 442 | | :- else. |
| 443 | | clpfd_maximum(El,List) :- %print(maximum(El,List)),nl, |
| 444 | | maximum(El,List). |
| 445 | | %force_post_constraint( maximum(El,List)). %, print(done(El)). |
| 446 | | :- endif. |
| 447 | | |
| 448 | | :- if(\+ predicate_property(minimum(_,_), _)). |
| 449 | | clpfd_minimum(_El,_List). % the current usage in minimum_of_set only uses this as additional constraint |
| 450 | | % TODO: provide a real implementation in SWI |
| 451 | | :- else. |
| 452 | | clpfd_minimum(El,List) :- %print(minimum(El,List)),nl, |
| 453 | | %force_post_constraint( minimum(El,List)). |
| 454 | | minimum(El,List). |
| 455 | | :- endif. |
| 456 | | |
| 457 | | |
| 458 | | :- assert_must_succeed((clpfd_interface:clpfd_if_then_else(pred_true,1,2,X), X==1)). |
| 459 | | :- assert_must_succeed((clpfd_interface:clpfd_if_then_else(pred_false,1,2,X), X==2)). |
| 460 | | :- assert_must_succeed_any((clpfd_interface:clpfd_if_then_else(_,21,21,X), X==21)). |
| 461 | | |
| 462 | | clpfd_if_then_else(_,ThenValue,ElseValue,Value) :- ThenValue==ElseValue,!, |
| 463 | | Value=ThenValue. |
| 464 | | clpfd_if_then_else(PredRes,ThenValue,ElseValue,Value) :- |
| 465 | | % TO DO: catch overflows and revert to simpler treatment then |
| 466 | | element(Idx,[ThenValue,ElseValue],Value), % Idx=1 if Value=ThenValue; Idx=2 if Value=ElseValue |
| 467 | | prop_12(PredRes,Idx). % if PredRes=pred_true -> Idx must be 1, 2 otherwise |
| 468 | | |
| 469 | | :- block prop_12(-,-). |
| 470 | | prop_12(P,V12) :- var(P),!,(V12=1 -> P=pred_true ; P=pred_false). |
| 471 | | prop_12(pred_true,1). |
| 472 | | prop_12(pred_false,2). |
| 473 | | |
| 474 | | /* |
| 475 | | catch(list_to_fdset([0,8589934592],R),E,true) |
| 476 | | E= error(domain_error(integer_list,[0,8589934592]),domain_error(list_to_fdset([0,8589934592],_A),1,integer_list,[0,8589934592])) |
| 477 | | domain_error |
| 478 | | */ |
| 479 | | |
| 480 | | |
| 481 | | clpfd_reify_inlist(El,FDList,FDRes, Posted) :- |
| 482 | | post_constraint2( (list_to_fdset(FDList,FDSET), (El in_set FDSET) #<=> FDRes), Posted). |
| 483 | | |
| 484 | | |
| 485 | | try_post_constraint(_) :- preferences:preference(use_clpfd_solver,false),!. |
| 486 | | :- if( environ(enable_time_out_for_constraints, true)). |
| 487 | | try_post_constraint(C) :- % print(posting(C)),nl, % |
| 488 | | catch(time_out_constraint(C,true), error(Err,_), check_error(Err,C)). |
| 489 | | % Note: this will only catch exceptions that occur while posting ! |
| 490 | | % CLPFD integer overflow exceptions can still happen after posting :-( |
| 491 | | :- else. |
| 492 | | try_post_constraint(C) :- % print(posting(C)),nl, % |
| 493 | ? | catch(C, error(Err,_), check_error(Err,C)). |
| 494 | | :- endif. |
| 495 | | |
| 496 | | post_constraint2(_,Posted) :- |
| 497 | | preferences:preference(use_clpfd_solver,false),!,Posted=false. |
| 498 | | :- if( environ(enable_time_out_for_constraints, true)). |
| 499 | | post_constraint2(C,Posted) :- % print(posting(C)),nl, % |
| 500 | | catch(time_out_constraint(C,(Posted=false)), error(Err,_), (check_error(Err,C),Posted=false)), |
| 501 | | (Posted==false -> true ; Posted=true). |
| 502 | | :- else. |
| 503 | | post_constraint2(C,Posted) :- % print(posting(C)),nl, % |
| 504 | ? | catch((C,Posted=true), error(Err,_), (check_error(Err,C),Posted=false)). |
| 505 | | :- endif. |
| 506 | | |
| 507 | | |
| 508 | | check_error(representation_error(ErrMsg),C) :- |
| 509 | | is_clpfd_overflow_representation_error_msg(ErrMsg), |
| 510 | | !, |
| 511 | | print_message('Ignoring constraint (clpfd overflow):'), |
| 512 | | print_message(C). |
| 513 | | check_error(domain_error(_Type,_Val),C) :- !, print_message('Ignoring constraint (domain error):'), |
| 514 | | % print(Type), print(' : '), print(Val), print(' : '), |
| 515 | | print_message(C). |
| 516 | | check_error(Err,C) :- |
| 517 | | %print_message('Ignoring constraint (unknown error):'), print(Err), print(' : '), print_message(C), |
| 518 | | add_error(clpfd_interface,'Ignoring constraint (unknown error):',Err:C). |
| 519 | | |
| 520 | | |
| 521 | | :- assert_must_succeed((print('Is 64-bit: '),(clpfd_interface:is_64_bit_system -> print('Yes') ; print('NO')),nl)). |
| 522 | | |
| 523 | | is_64_bit_system :- |
| 524 | | % 268435456 = 2^28, will generate overflow in 32-bit system |
| 525 | | catch(_X #= 268435456, error(representation_error(_),_), fail). |
| 526 | | |
| 527 | | |
| 528 | | clpfd_overflow_error_message :- add_clpfd_overflow_message(error). |
| 529 | | clpfd_overflow_warning_message :- add_clpfd_overflow_message(warning). |
| 530 | | |
| 531 | | add_clpfd_overflow_message(error) :- !, clpfd_overflow_msg(M), |
| 532 | | add_error(clpfd_overflow,M). |
| 533 | | add_clpfd_overflow_message(message) :- !, clpfd_overflow_msg(M), |
| 534 | | add_message(clpfd_overflow,M). |
| 535 | | add_clpfd_overflow_message(silent) :- !, _X #= 1. % needed to somehow clear overflow in SICStus, see x>1 & y:NATURAL & y=x+x & z={y,x} (test 2142) |
| 536 | | add_clpfd_overflow_message(warning) :- !, clpfd_overflow_msg(M), |
| 537 | | add_warning(clpfd_overflow,M). |
| 538 | | add_clpfd_overflow_message(Unknown) :- |
| 539 | | add_internal_error('Illegal call: ',add_clpfd_overflow_message(Unknown)). |
| 540 | | |
| 541 | | %clpfd_overflow_print_message :- clpfd_overflow_msg(M),write(user_error,M),nl(user_error). |
| 542 | | |
| 543 | | integer_too_small_for_clpfd(X) :- current_prolog_flag(min_tagged_integer,Min), X<Min. |
| 544 | | integer_too_large_for_clpfd(X) :- current_prolog_flag(max_tagged_integer,Max), X>Max. |
| 545 | | |
| 546 | | |
| 547 | | % post_constraint3(CLPFD,PrologBackup,Posted) |
| 548 | | post_constraint3(_,C,Posted) :- preferences:preference(use_clpfd_solver,false),!,Posted=false,call(C). |
| 549 | | post_constraint3(C,PrologBackup,Posted) :- % print(posting(C,PrologBackup)),nl, |
| 550 | | catch( |
| 551 | | time_out_constraint((C,Posted=true),(Posted=false,PrologBackup)), |
| 552 | | error(Err,_), |
| 553 | | (check_error(Err,C),Posted=false,call(PrologBackup))). |
| 554 | | |
| 555 | | % post_constraint(CLPFD,PrologBackup) |
| 556 | ? | post_constraint(_,C) :- preferences:preference(use_clpfd_solver,false),!,call(C). |
| 557 | | :- if(environ(enable_time_out_for_constraints, true)). |
| 558 | | post_constraint(C,PrologBackup) :- % print(posting(C,PrologBackup)),nl, |
| 559 | | catch(time_out_constraint(C,PrologBackup), error(Err,_), (check_error(Err,C),call(PrologBackup))). |
| 560 | | :- else. |
| 561 | | post_constraint(C,PrologBackup) :- % print(posting(C,PrologBackup)),nl, |
| 562 | ? | catch(C, error(Err,_), (check_error(Err,C),call(PrologBackup))). |
| 563 | | :- endif. |
| 564 | | |
| 565 | | % no use_clpfd_solver check: |
| 566 | | force_post_constraint(C) :- call(C). % to use fd_batch(C) we need to convert C to list |
| 567 | | force_post_constraint(C,PrologBackup) :- % print(posting(C,PrologBackup)),nl, |
| 568 | | catch(time_out_constraint(C,PrologBackup), error(Err,_), (check_error(Err,C),call(PrologBackup))). |
| 569 | | |
| 570 | | |
| 571 | | :- if(\+ environ(enable_time_out_for_constraints, true)). |
| 572 | | time_out_constraint(C,_) :- call(C). % to use fd_batch(C) we need to convert C to list |
| 573 | | :- else. |
| 574 | | % Note: time_out/3 is quite expensive ! |
| 575 | | time_out_constraint(C,PrologBackup) :- %% debug:new_pp(C,PP), %% |
| 576 | | %%preferences:preference(time_out,CurTO), TO is 800 + (CurTO//100), time_out(C,TO,T), |
| 577 | | preferences:preference(solver_strength,Strength), |
| 578 | | TO is 1200 + Strength*200, |
| 579 | | time_out(C,TO,T), %% debug:new_sol(C,PP), %% |
| 580 | | (T==time_out -> print_message('Timeout when posting constraint:'), print_message(C), |
| 581 | | (preferences:preference(fail_if_clpfd_timeout,true) |
| 582 | | -> print_message('Assuming unsatisfiable !'),fail |
| 583 | | ; call(PrologBackup)) |
| 584 | | ; true). |
| 585 | | :- endif. |
| 586 | | |
| 587 | | |
| 588 | | :- assert_must_succeed_any((clpfd:in(X,1..4), clpfd_interface:clpfd_domain(X,L,U), L==1, U==4)). |
| 589 | | :- assert_must_succeed((clpfd_interface:clpfd_domain(3,L,U), L==3, U==3)). |
| 590 | | clpfd_domain(Var,Low,Up) :- var(Var),!, |
| 591 | | fd_min(Var,Low), fd_max(Var,Up). |
| 592 | | % one could call fd_set(Var,Set), fdset_parts(Set,Min,Max,_Rest) or undocumented clpfd:fd_min_max(Var,Low,Up). |
| 593 | | % used to check (preferences:preference(use_clpfd_solver,false) -> Low=inf,Up=sup ; fd_min(Var,Low), fd_max(Var,Up) ). |
| 594 | | % but can also be called when CLPFD false for global_set values |
| 595 | | %used to call: fd_dom(Var, Low..Up)). fd_dom will sometimes return {1} \/ 2..3 or something like that ! |
| 596 | | clpfd_domain(X,X,X). |
| 597 | | |
| 598 | | % allows to have Up as inf |
| 599 | | :- assert_must_succeed_any((clpfd_interface:clpfd_in_domain(X,1,4), clpfd_interface:clpfd_domain(X,L,U), L==1, U==4)). |
| 600 | | :- assert_must_succeed_any((clpfd_interface:clpfd_in_domain(X,1,inf), |
| 601 | | clpfd_interface:clpfd_domain(X,L,U), L==1, U==sup)). |
| 602 | | clpfd_in_domain(Var,Low,Up) :- Up==inf,!, % we use inf as infinity not as infinum |
| 603 | | Var #>= Low. |
| 604 | | clpfd_in_domain(Var,Low,Up) :- |
| 605 | | Var in Low..Up. |
| 606 | | |
| 607 | | |
| 608 | | % find some element in the domain of a FD variable |
| 609 | | :- assert_must_succeed((clpfd_interface:clpfd_some_element_in_domain(1,X), X==1)). |
| 610 | | :- assert_must_succeed_any((clpfd:in(X,1..4), clpfd_interface:clpfd_some_element_in_domain(X,_))). |
| 611 | | :- assert_must_fail((clpfd:in(X,1..4), clpfd:in(Y,5..6), |
| 612 | | clpfd_interface:clpfd_some_element_in_domain(X,E), |
| 613 | | clpfd_interface:clpfd_some_element_in_domain(Y,E))). |
| 614 | | clpfd_some_element_in_domain(Var,El) :- nonvar(Var),!, El=Var. |
| 615 | | clpfd_some_element_in_domain(Var,El) :- fd_set(Var,Set), fdset_parts(Set,Min,Max,_Rest), |
| 616 | | (number(Min) -> El=Min ; number(Max),El=Max ; El=0). |
| 617 | | /* the latter should always succeed; here are some example calls: |
| 618 | | | ?- X #\= 3, fd_set(X,S), fdset_parts(S,Min,Max,Parts). |
| 619 | | S = [[inf|2],[4|sup]], |
| 620 | | Min = inf, |
| 621 | | Max = 2, |
| 622 | | Parts = [[4|sup]], |
| 623 | | |
| 624 | | | ?- fd_set(X,S), fdset_parts(S,Min,Max,Parts). |
| 625 | | S = [[inf|sup]], |
| 626 | | Min = inf, |
| 627 | | Max = sup, |
| 628 | | Parts = [] |
| 629 | | */ |
| 630 | | |
| 631 | | |
| 632 | | % succeed if we have a number or a CLPFD Var whose upper value is bounded |
| 633 | | :- assert_must_succeed_any((clpfd:in(X,1..4), clpfd_interface:clpfd_max_bounded(X))). |
| 634 | | :- assert_must_succeed(clpfd_interface:clpfd_max_bounded(4)). |
| 635 | | :- assert_must_fail( clpfd_interface:clpfd_max_bounded(_) ). |
| 636 | | clpfd_max_bounded(Var) :- var(Var), !, fd_max(Var,Up), number(Up). |
| 637 | | clpfd_max_bounded(N) :- number(N). |
| 638 | | |
| 639 | | :- assert_must_succeed(clpfd_interface:clpfd_can_match(1,1)). |
| 640 | | :- assert_must_succeed(clpfd_interface:clpfd_can_match(11,_X)). |
| 641 | | :- assert_must_succeed(clpfd_interface:clpfd_can_match(_X,22)). |
| 642 | | :- assert_must_succeed(clpfd_interface:clpfd_can_match(X,X)). |
| 643 | | :- assert_must_fail(clpfd_interface:clpfd_can_match(21,22)). |
| 644 | | :- assert_must_fail((dif(A,B),clpfd_interface:clpfd_can_match(A,B))). |
| 645 | | % check if two numbers or fd variables can match without unifying (as this could trigger propagations) |
| 646 | | % TODO: merge with fd_frozen_dif |
| 647 | | clpfd_can_match(Var1,Var2) :- var(Var1),!, (var(Var2) -> clpfd_var_can_unify(Var1,Var2) ; clpfd_can_match_nr(Var1,Var2)). |
| 648 | | clpfd_can_match(Nr1,Var2) :- var(Var2),!,clpfd_can_match_nr(Var2,Nr1). |
| 649 | | clpfd_can_match(Nr1,Nr2) :- Nr1=Nr2. |
| 650 | | 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 |
| 651 | | fd_set(Var,FDS), Nr in_set FDS. |
| 652 | | |
| 653 | | clpfd_var_can_unify(Var1,Var2) :- |
| 654 | | (Var1==Var2 -> true |
| 655 | | ; fd_set(Var1,FDS1), fd_set(Var2,FDS2), fdset_intersect(FDS1,FDS2), |
| 656 | ? | \+ kernel_dif:frozen_dif(Var1,Var2) |
| 657 | | ). |
| 658 | | |
| 659 | | % check if FD variables or numbers have common possible value |
| 660 | | % called by fd_frozen_dif |
| 661 | | :- assert_must_succeed_any((clpfd:in(X,1..4), clpfd_interface:clpfd_can_intersect(X,X))). |
| 662 | | :- assert_must_succeed_any((clpfd:in(X,1..4), clpfd:in(Y,0..1), clpfd_interface:clpfd_can_intersect(X,Y))). |
| 663 | | :- assert_must_succeed_any((clpfd:in(X,1..4), clpfd:in(Y,1..1), clpfd_interface:clpfd_can_intersect(X,Y))). |
| 664 | | :- assert_must_succeed((clpfd:in(X,4..4), clpfd:in(Y,4..4), clpfd_interface:clpfd_can_intersect(X,Y))). |
| 665 | | :- assert_must_succeed_any((clpfd:in(X,4..4), clpfd:in(Y,0..6), clpfd_interface:clpfd_can_intersect(X,Y))). |
| 666 | | :- assert_must_succeed_any((clpfd:in(X,1..4), clpfd_interface:clpfd_can_intersect(X,_))). |
| 667 | | :- assert_must_succeed_any( clpfd_interface:clpfd_can_intersect(_X,_Y)). |
| 668 | | :- assert_must_fail((clpfd:in(X,2..4), clpfd:in(Y,0..1), clpfd_interface:clpfd_can_intersect(X,Y))). |
| 669 | | :- assert_must_fail((clpfd:in(X,2..4), clpfd:in(Y,0..0), clpfd_interface:clpfd_can_intersect(X,Y))). |
| 670 | | :- assert_must_fail((clpfd:in(X,1..1), clpfd:in(Y,0..0), clpfd_interface:clpfd_can_intersect(X,Y))). |
| 671 | | clpfd_can_intersect(Number1,NrOrVar2) :- nonvar(Number1),!, |
| 672 | | (nonvar(NrOrVar2) -> Number1==NrOrVar2 |
| 673 | | ; fd_set(NrOrVar2,FDS2), fdset_member(Number1,FDS2)). |
| 674 | | clpfd_can_intersect(Var1,Number2) :- nonvar(Number2),!, |
| 675 | | fd_set(Var1,FDS1), fdset_member(Number2,FDS1). |
| 676 | | clpfd_can_intersect(Var1,Var2) :- |
| 677 | | fd_set(Var1,FDS1), fd_set(Var2,FDS2), fdset_intersect(FDS1,FDS2). |
| 678 | | |
| 679 | | % check if we can currently determine an integer to be greater or equal to a given number |
| 680 | | clpfd_check_geq_nr(X,Nr) :- |
| 681 | | number(X),!, |
| 682 | | X>=Nr. |
| 683 | | clpfd_check_geq_nr(X,_) :- |
| 684 | | (nonvar(X) % something like X+Y |
| 685 | | ; preferences:preference(use_clpfd_solver,false)), |
| 686 | | !,fail. |
| 687 | | clpfd_check_geq_nr(X,Nr) :- |
| 688 | | fd_min(X,Low), number(Low), Low>=Nr. |
| 689 | | |
| 690 | | :- assert_must_succeed_any((clpfd:in(X,1..4), clpfd_interface:clpfd_size(X,Sz), Sz==4)). |
| 691 | | :- assert_must_succeed_any((clpfd:in(X,1..1), clpfd_interface:clpfd_size(X,Sz), Sz==1)). |
| 692 | | clpfd_size(Var,Size) :- integer(Var),!, Size=1. % fd_size(1152921504606846976,X) creates overflow in SICStus 4.7 |
| 693 | | clpfd_size(Var,Size) :- var(Var),!,fd_size(Var,Size). % also works for free variables, returning sup |
| 694 | | clpfd_size(Var,Size) :- add_internal_error('Illegal FD variable:',clpfd_size(Var,Size)), Size=1. |
| 695 | | %clpfd_size(Var,Size) :- (preferences:preference(use_clpfd_solver,false) |
| 696 | | % -> Size = sup ; fd_size(Var,Size) ). |
| 697 | | |
| 698 | | :- assert_must_succeed(clpfd_interface:clpfd_alldifferent([1,2,3])). |
| 699 | | :- assert_must_succeed((preferences:preference(use_clpfd_solver,false) -> true ; clpfd:in(X,1..4), clpfd_interface:clpfd_alldifferent([1,X,2,3]), X==4)). |
| 700 | | :- assert_must_succeed(clpfd_interface:all_dif([1,2,3],[])). |
| 701 | | :- assert_must_fail(clpfd_interface:clpfd_alldifferent([1,2,3,2])). |
| 702 | | :- assert_must_fail((preferences:preference(use_clpfd_solver,true), clpfd:in(X,1..3), clpfd_interface:clpfd_alldifferent([1,X,2,3]))). |
| 703 | | :- assert_must_fail(clpfd_interface:all_dif([1,2,3,2],[])). |
| 704 | | |
| 705 | | clpfd_alldifferent([]) :- !. |
| 706 | | clpfd_alldifferent(L) :- post_constraint(all_different(L),all_dif(L,[])). |
| 707 | | |
| 708 | | all_dif([],_). |
| 709 | | all_dif([H|T],All) :- all_dif_aux(All,H), all_dif(T,[H|All]). |
| 710 | | |
| 711 | | all_dif_aux([],_). |
| 712 | | all_dif_aux([H|T],X) :- dif(H,X), all_dif_aux(T,X). |
| 713 | | |
| 714 | | |
| 715 | | |
| 716 | | clpfd_labeling(Variables,Options) :- |
| 717 | | % Options = [ffc,enum] or step, bisect, interval |
| 718 | ? | catch(labeling(Options,Variables), error(instantiation_error,Err), ( |
| 719 | | print(Err),nl, |
| 720 | | % this can happen when CLP(FD) has a time-out for other constraints |
| 721 | | add_error(clpfd_interface,'CLP(FD) Variables not set up for ',labeling(Options,Variables)) |
| 722 | | )). |
| 723 | | |
| 724 | | |
| 725 | | clpfd_in_domain(Variable) :- preferences:preference(randomise_enumeration_order,true),!, |
| 726 | ? | clpfd_randomised_in_domain(Variable). |
| 727 | | clpfd_in_domain(Variable) :- !, |
| 728 | ? | catch(indomain(Variable), |
| 729 | | % Assigns via backtracking a feasible value to X. |
| 730 | | % For integer arguments, the values are assigned in increasing order. |
| 731 | | error(instantiation_error,Err), ( |
| 732 | | print(Err),nl, |
| 733 | | % this can happen when CLP(FD) has a time-out for other constraints |
| 734 | | add_error(clpfd_interface,'CLP(FD) Variables not set up for ',indomain(Variable)) |
| 735 | | )). |
| 736 | | |
| 737 | | :- if((current_prolog_flag(dialect, sicstus), |
| 738 | | current_prolog_flag(version_data, sicstus(4,VN,_,_,_)), VN>=10)). |
| 739 | | % this uses the new labeling option in SICStus 4.10 instead of the C extension |
| 740 | | % however, this seems a bit slower on unconstrained variables: |
| 741 | | % statistics(walltime,_), (random_permutations:enum_fd_random(N,0,200000),fail ; statistics(walltime,W2)). 52 ms |
| 742 | | % statistics(walltime,_), (clpfd:domain([N],0,200000),clpfd:labeling([enum,random],[N]),fail ; statistics(walltime,W2)). 124 ms |
| 743 | | % but is faster on constrained variables: |
| 744 | | % statistics(walltime,_), (clpfd:domain([N],0,200000),random_permutations:enum_fd_random(N,0,200000),fail ; statistics(walltime,W2)). 124 ms |
| 745 | | % statistics(walltime,_), (clpfd:domain([N],0,200000), clpfd:'#>'(N,100000), random_permutations:enum_fd_random(N,0,200000),fail ; statistics(walltime,W2)). 112 ms |
| 746 | | % statistics(walltime,_), (clpfd:domain([N],0,200000), clpfd:'#>'(N,100000),clpfd:labeling([enum,random],[N]),fail ; statistics(walltime,W2)). 76 ms |
| 747 | | clpfd_randomised_enum(Variable,Low,Up) :- |
| 748 | | Variable in Low..Up, |
| 749 | | clpfd_randomised_in_domain(Variable). |
| 750 | | clpfd_randomised_in_domain(Variable) :- !, |
| 751 | | catch(labeling([enum,random],[Variable]), |
| 752 | | error(instantiation_error,Err), ( |
| 753 | | print(Err),nl, |
| 754 | | % this can happen when CLP(FD) has a time-out for other constraints |
| 755 | | add_error(clpfd_interface,'CLP(FD) Variables not set up for ',clpfd_randomised_in_domain(Variable)) |
| 756 | | )). |
| 757 | | :- else. |
| 758 | | :- use_module(library(random)). |
| 759 | | :- use_module(extension('random_permutations/random_permutations'), |
| 760 | | [enum_fd_random/3]). |
| 761 | | clpfd_randomised_enum(Var,Low,Up) :- |
| 762 | ? | enum_fd_random(Var,Low,Up). |
| 763 | | clpfd_randomised_in_domain(Var) :- |
| 764 | | clpfd_domain(Var,Low,Up), |
| 765 | | (number(Low),number(Up) |
| 766 | ? | -> enum_fd_random(Var,Low,Up) |
| 767 | | ; add_internal_error('Unbounded FD variable: ',clpfd_randomised_in_domain(Var)), |
| 768 | | %trace, kernel_waitflags:get_fd_priority(Var,_), |
| 769 | | fail |
| 770 | | ). |
| 771 | | :- endif. |
| 772 | | |
| 773 | | clpfd_degree(Variable,Degree) :- fd_degree(Variable,Degree). % can be called with plain variables and with numbers |
| 774 | | |
| 775 | | % a new predicate to obtain the next variable that would be enumerated |
| 776 | | % (useful if you want to interleave other labeling with CLPFD labeling) |
| 777 | | |
| 778 | | :- assert_must_succeed((X in 1..3, Y in 3..5, X #> Z, clpfd_interface:clpfd_get_next_variable_to_label([Y,2,X,3],V),V==X, X=3, Y=3, Z=2)). |
| 779 | | clpfd_get_next_variable_to_label(Variables,Var) :- |
| 780 | | preference(clpfd_solver_label_option,Option), % was ffc |
| 781 | | clpfd_get_next_variable_to_label(Variables,Var,Option). |
| 782 | | |
| 783 | | :- if(predicate_property(clpfd:'$fd_delete'(_, _, _), _)). |
| 784 | | % possible options : ff, ffc, step, enum, bisect, median, ..., max_regret, impact, dom_w_deg |
| 785 | | clpfd_get_next_variable_to_label(Variables,Var,Opt) :- |
| 786 | | Variables \= [], % there is a segmentation fault if we call fd_delete with empty list |
| 787 | | % this also causes segmentation fault: |
| 788 | | % 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. |
| 789 | | clpfd:'$fd_delete'(Variables,Var,Opt). |
| 790 | | :- else. |
| 791 | | clpfd_get_next_variable_to_label(Variables,Var,_) :- % ignores option; can only do ffc |
| 792 | | clpfd_get_next_variable_to_label_ffc(Variables,Var,_). |
| 793 | | % a version of the above just for ffc, re-implemented by hand so as not to be dependent on internal predicates of clpfd |
| 794 | | % also: it returns the remaining variables (filters out non-variables) |
| 795 | | % but it can be much slower than clpfd:$fd_delete (e.g., NQueens50) |
| 796 | | clpfd_get_next_variable_to_label_ffc([H|T],Var,RestV) :- |
| 797 | | (get_fd_info(H,HI) -> get_next_aux(T,H,HI,Var,RestV) |
| 798 | | %,((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) |
| 799 | | ; clpfd_get_next_variable_to_label_ffc(T,Var,RestV) |
| 800 | | ). |
| 801 | | % , tools_printing:print_vars([H|T]),print(sel(Var,RestV)),nl. |
| 802 | | |
| 803 | | get_next_aux([],Var,_,Var,[]). |
| 804 | | get_next_aux([H|T],VarSoFar,VInfo,Var,Rest) :- |
| 805 | | (get_fd_info(H,HI) |
| 806 | | -> (HI @< VInfo -> Rest = [VarSoFar|TRest], get_next_aux(T,H,HI,Var,TRest) |
| 807 | | ; Rest = [H|TRest], get_next_aux(T,VarSoFar,VInfo,Var,TRest) |
| 808 | | ) |
| 809 | | ; get_next_aux(T,VarSoFar,VInfo,Var,Rest)). |
| 810 | | |
| 811 | | get_fd_info(Var,(Sz,NDg)) :- fd_size(Var,Sz), Sz \= 1, % note: we have 2 @<sup |
| 812 | | % kernel_waitflags:size_of_attached_goals(Var,Dg), |
| 813 | | fd_degree(Var,Dg), |
| 814 | | NDg is -Dg. % negate to give higher priority to goals with high degree |
| 815 | | :- endif. |
| 816 | | |
| 817 | | |
| 818 | ? | catch_clpfd_overflow_call1(Call) :- catch_clpfd_overflow_call3(Call,error,fail). |
| 819 | ? | catch_clpfd_overflow_call2(Call,Handler) :- catch_clpfd_overflow_call3(Call,error,Handler). |
| 820 | | catch_clpfd_overflow_call3(Call,Type,Handler) :- |
| 821 | ? | catch(Call, |
| 822 | | error(_ErrMsg,ErrTerm), % the ErrMsg can be representation_error or domain_error |
| 823 | | handle_representation_error(ErrTerm,Type,Handler)). |
| 824 | | |
| 825 | | handle_representation_error(ErrTerm,Type,Call) :- |
| 826 | | (is_clpfd_overflow_representation_error(ErrTerm,_) |
| 827 | | -> add_clpfd_overflow_message(Type), |
| 828 | | Call |
| 829 | | ; throw(representation_error(ErrTerm))). |
| 830 | | |
| 831 | | :- use_module(debug,[debug_println/2]). |
| 832 | | catch_and_ignore_clpfd_overflow(PP,Call) :- |
| 833 | ? | catch(Call, |
| 834 | | error(_,ErrTerm), |
| 835 | | (is_clpfd_overflow_representation_error(ErrTerm,_) |
| 836 | | -> debug_println(19,ignoring_clpfd_overflow(PP,ErrTerm)) |
| 837 | | ; throw(representation_error(ErrTerm)) |
| 838 | | )). |
| 839 | | |
| 840 | | |
| 841 | | % ----------------------- |
| 842 | | % a debugging utility: |
| 843 | | |
| 844 | | % observe changes in domains for a CLP(FD) variable: |
| 845 | | clpfd_observe(Variable,Name) :- nonvar(Name),!, |
| 846 | | fd_global(clpfd_observe(Variable,Name),inst(0),[dom(Variable)]). |
| 847 | | clpfd_observe(Variable,Name) :- |
| 848 | | add_internal_error('Illegal var name:',clpfd_observe(Variable,Name)). |
| 849 | | |
| 850 | | :- multifile clpfd:dispatch_global/4. |
| 851 | | clpfd:dispatch_global(clpfd_observe(Var,Name), inst(Nr), inst(N1), Actions) :- !, |
| 852 | | fd_dom(Var,Dom), |
| 853 | | format(' -(~w)-> clpfd var ~w : ~w : ~w~n',[Nr,Name,Var,Dom]), |
| 854 | | N1 is Nr+1, |
| 855 | | Actions=[]. |
| 856 | | |
| 857 | | % observe bindings in a B state: |
| 858 | | clpfd_observe_state([]). |
| 859 | | clpfd_observe_state([bind(Name,Var)|T]) :- |
| 860 | | clpfd_observe_value(Var,Name), |
| 861 | | clpfd_observe_state(T). |
| 862 | | |
| 863 | | :- block clpfd_observe_value(-,?). |
| 864 | | clpfd_observe_value(int(Var),Name) :- !, clpfd_observe(Var,Name). |
| 865 | | clpfd_observe_value(fd(Var,_T),Name) :- !, clpfd_observe(Var,Name). |
| 866 | | % TODO: more types |
| 867 | | clpfd_observe_value(_,_). |
| 868 | | |
| 869 | | /* |
| 870 | | Testing for how large the integers can be, 32-bit system: |
| 871 | | | ?- X is integer(2**30), Y#=X+1. |
| 872 | | ! Representation error in argument 2 of user:'t=u+c'/3 |
| 873 | | ! CLPFD integer overflow |
| 874 | | ! goal: 't=u+c'(_102,1073741824,1) |
| 875 | | | ?- X is integer(2**30), Y#=X. |
| 876 | | X = 1073741824, |
| 877 | | Y = 1073741824 ? |
| 878 | | 64-bit system: |
| 879 | | | ?- X is integer(2**60), Y#=X. |
| 880 | | ! Representation error in argument 2 of user:(#=)/2 |
| 881 | | ! CLPFD integer overflow |
| 882 | | ! goal: _413#=1152921504606846976 |
| 883 | | |
| 884 | | | ?- X is integer(2**60)-1, Y#=X. |
| 885 | | X = 1152921504606846975, |
| 886 | | Y = 1152921504606846975 ? |
| 887 | | yes |
| 888 | | */ |
| 889 | | |
| 890 | | % --------------- |
| 891 | | |
| 892 | | % a few other arithmetic operators provided in external functions |
| 893 | | |
| 894 | | :- assert_must_succeed(clpfd_interface:clpfd_gcd(6,3,3)). |
| 895 | | :- assert_must_succeed(clpfd_interface:clpfd_gcd(6,-3,3)). |
| 896 | | :- assert_must_succeed((clpfd_interface:clpfd_gcd(X,Y,R), X=12, Y=10, R==2)). |
| 897 | | :- assert_must_fail((preferences:preference(use_clpfd_solver,true),clpfd_interface:clpfd_gcd(X,_,R), X=12, R==13)). |
| 898 | | :- assert_must_fail((preferences:preference(use_clpfd_solver,true),clpfd_interface:clpfd_gcd(_,Y,R), Y=12, R==13)). |
| 899 | | % Greatest Common Divisor (largest common factor) |
| 900 | | clpfd_gcd(X,Y,R) :- |
| 901 | | gcd2(X,Y,R), |
| 902 | | (var(R) -> try_post_constraint((R #>0, R #=< abs(X), R#=< abs(Y))) ; true). |
| 903 | | :- block gcd2(?,-,?),gcd2(-,?,?). |
| 904 | | gcd2(X,Y,R) :- R is gcd(X,Y). |
| 905 | | |
| 906 | | :- assert_must_succeed(clpfd_interface:clpfd_lcm(6,3,6)). |
| 907 | | :- assert_must_succeed(clpfd_interface:clpfd_lcm(6,-3,6)). |
| 908 | | :- assert_must_succeed((clpfd_interface:clpfd_lcm(X,Y,R), X=12, Y=10, R==60)). |
| 909 | | :- assert_must_fail((preferences:preference(use_clpfd_solver,true),clpfd_interface:clpfd_lcm(X,_,R), X=12, R==11)). |
| 910 | | :- assert_must_fail((preferences:preference(use_clpfd_solver,true),clpfd_interface:clpfd_lcm(_,Y,R), Y=12, R==11)). |
| 911 | | % Least Common Multiple |
| 912 | | clpfd_lcm(X,Y,R) :- |
| 913 | | lcm2(X,Y,R), |
| 914 | | (var(R) -> try_post_constraint((R #>=abs(X), R #>= abs(Y), R#=< abs(X*Y))) ; true). |
| 915 | | |
| 916 | | :- block lcm2(?,-,?),lcm2(-,?,?). |
| 917 | | lcm2(X,Y,R) :- CD is gcd(X,Y), R is (abs(X*Y) // CD). |
| 918 | | |
| 919 | | :- assert_must_succeed((clpfd_interface:clpfd_abs(6,R), R == 6)). |
| 920 | | :- assert_must_succeed((clpfd_interface:clpfd_abs(Y,R), Y = -6, R == 6)). |
| 921 | | :- assert_must_fail((preferences:preference(use_clpfd_solver,true),clpfd_interface:clpfd_abs(_,R), R = -1)). |
| 922 | | :- assert_must_fail((preferences:preference(use_clpfd_solver,true),clpfd_interface:clpfd_abs(X,R), R=0, X \== 0)). |
| 923 | | % Absolute value |
| 924 | | clpfd_abs(X,R) :- preferences:preference(use_clpfd_solver,true),!, |
| 925 | | clpfd_eq_expr(R,abs(X)). |
| 926 | | clpfd_abs(X,R) :- clpfd_abs2(X,R). |
| 927 | | :- block clpfd_abs2(-,?). |
| 928 | | clpfd_abs2(X,R) :- R is abs(X). |
| 929 | | |
| 930 | | :- assert_must_succeed((clpfd_interface:clpfd_sign(6,R), R == 1)). |
| 931 | | :- assert_must_succeed((clpfd_interface:clpfd_sign(Y,R), Y = -6, R == -1)). |
| 932 | | :- assert_must_succeed((clpfd_interface:clpfd_sign(Y,R), Y = 0, R == 0)). |
| 933 | | :- assert_must_fail((preferences:preference(use_clpfd_solver,true),clpfd_interface:clpfd_sign(_,R), R = -2)). |
| 934 | | :- assert_must_fail((preferences:preference(use_clpfd_solver,true),clpfd_interface:clpfd_sign(X,R), R=0, X \== 0)). |
| 935 | | |
| 936 | | clpfd_sign(X,R) :- |
| 937 | | sign2(X,R), |
| 938 | | (var(R) -> try_post_constraint((R in -1..1, X #> 0 #<=> R #= 1, X #< 0 #<=> R #= -1, X #= 0 #<=> R #= 0)) ; true). |
| 939 | | |
| 940 | | :- block sign2(-,?). |
| 941 | | sign2(X,R) :- R is sign(X). |
| 942 | | |
| 943 | | |
| 944 | | % BitOp is something like xor(X,Y), ... which are now supported in SICStus 4.9 |
| 945 | | %clpfd_bitwise_operator(BitOp,R) :- preferences:preference(use_clpfd_solver,true),!, |
| 946 | | % clpfd_eq_expr(R,BitOp). |
| 947 | | %clpfd_bitwise_operator(X,R) :- when(nonvar(X),R is X). |
| 948 | | |
| 949 | | |