Commit 659ea9b8e9c81147569915917ad2e1e18caffd5d
1 parent
acfea957
Exists in
master
new constraints poly-eq-k, poly-ne, and poly-ne-k
Showing
6 changed files
with
1022 additions
and
0 deletions
Show diff stats
README
| ... | ... | @@ -173,6 +173,9 @@ Global constraints |
| 173 | 173 | fd_sum_prod(fd_int X[], fd_int Y[], int n, int k) X . Y = k |
| 174 | 174 | |
| 175 | 175 | fd_poly_eq(int C[], fd_int Y[], int n, fd_int z) z <= C . Y <= z |
| 176 | + fd_poly_eq_k(int C[], fd_int Y[], int n, int k) C . Y = k | |
| 177 | + fd_poly_ne(int C[], fd_int Y[], int n, fd_int z) C . Y != z | |
| 178 | + fd_poly_ne_k(int C[], fd_int Y[], int n, int k) C . Y != k | |
| 176 | 179 | fd_knapsack2(fd_int X[], fd_int Y[], int n, fd_int z) z <= X . Y <= z |
| 177 | 180 | |
| 178 | 181 | fd_exactly(fd_int X[], int n, int c, int k) #{ i | X[i] = k } = c | ... | ... |
src/constraints.c
| ... | ... | @@ -149,6 +149,9 @@ void fd__constraint_free_memory() |
| 149 | 149 | #include <constraints/poly-eq.c> |
| 150 | 150 | #include <constraints/element-var.c> |
| 151 | 151 | #include <constraints/exactly-vars.c> |
| 152 | +#include <constraints/poly-eq-k.c> | |
| 153 | +#include <constraints/poly-ne.c> | |
| 154 | +#include <constraints/poly-ne-k.c> | |
| 152 | 155 | |
| 153 | 156 | #ifdef CONSTRAINT_CLASS |
| 154 | 157 | |
| ... | ... | @@ -178,6 +181,9 @@ _FD_CONSTRAINT_INITIALISATION(max, FD_CONSTR_MAX) |
| 178 | 181 | _FD_CONSTRAINT_INITIALISATION(poly_eq, FD_CONSTR_POLY_EQ) |
| 179 | 182 | _FD_CONSTRAINT_INITIALISATION(element_var, FD_CONSTR_ELEMENT_VAR) |
| 180 | 183 | _FD_CONSTRAINT_INITIALISATION(exactly_vars, FD_CONSTR_EXACTLY_VARS) |
| 184 | +_FD_CONSTRAINT_INITIALISATION(poly_eq_k, FD_CONSTR_POLY_EQ_K) | |
| 185 | +_FD_CONSTRAINT_INITIALISATION(poly_ne, FD_CONSTR_POLY_NE) | |
| 186 | +_FD_CONSTRAINT_INITIALISATION(poly_ne_k, FD_CONSTR_POLY_NE_K) | |
| 181 | 187 | |
| 182 | 188 | void _fd_init_constraints() |
| 183 | 189 | { |
| ... | ... | @@ -212,6 +218,9 @@ void _fd_init_constraints() |
| 212 | 218 | _fd_init_constraint(poly_eq); |
| 213 | 219 | _fd_init_constraint(element_var); |
| 214 | 220 | _fd_init_constraint(exactly_vars); |
| 221 | + _fd_init_constraint(poly_eq_k); | |
| 222 | + _fd_init_constraint(poly_ne); | |
| 223 | + _fd_init_constraint(poly_ne_k); | |
| 215 | 224 | |
| 216 | 225 | done = 1; |
| 217 | 226 | } | ... | ... |
src/constraints.h
| ... | ... | @@ -0,0 +1,469 @@ |
| 1 | +/* poly-eq-k(C, X, k) == sum(C . X) = k */ | |
| 2 | + | |
| 3 | +static int fd_poly_eq_k_filter(fd_constraint this) | |
| 4 | +{ | |
| 5 | + int k; | |
| 6 | + int min, max; | |
| 7 | + int terms = this->nvariables; | |
| 8 | + int *mins, *maxs; | |
| 9 | + int i; | |
| 10 | +#ifdef CONSTRAINT_TEMPS | |
| 11 | + int *base; | |
| 12 | + | |
| 13 | + assert(!fd__constraint_data_valid(this)); | |
| 14 | + | |
| 15 | + if (!constraint_memory[this->index]) | |
| 16 | + constraint_memory[this->index] = malloc((2 * terms + 2) * sizeof(int)); | |
| 17 | + | |
| 18 | + base = constraint_memory[this->index]; | |
| 19 | + | |
| 20 | + mins = base + 2; | |
| 21 | + maxs = mins + terms; | |
| 22 | +#else | |
| 23 | + mins = alloca(terms * sizeof(*mins)); | |
| 24 | + maxs = alloca(terms * sizeof(*maxs)); | |
| 25 | +#endif | |
| 26 | + | |
| 27 | + k = this->constants[terms]; | |
| 28 | + | |
| 29 | + // sum the minima and the maxima of the terms | |
| 30 | + min = max = 0; | |
| 31 | + | |
| 32 | + for (i = 0; i < terms; ++i) | |
| 33 | + { | |
| 34 | + int vl, vh; | |
| 35 | + | |
| 36 | + if (this->constants[i] > 0) | |
| 37 | + { | |
| 38 | + vl = mins[i] = _fd_var_min(VAR(this, i)); | |
| 39 | + vh = maxs[i] = _fd_var_max(VAR(this, i)); | |
| 40 | + } | |
| 41 | + else | |
| 42 | + { | |
| 43 | + vl = maxs[i] = _fd_var_max(VAR(this, i)); | |
| 44 | + vh = mins[i] = _fd_var_min(VAR(this, i)); | |
| 45 | + } | |
| 46 | + | |
| 47 | + min += this->constants[i] * vl; | |
| 48 | + max += this->constants[i] * vh; | |
| 49 | + } | |
| 50 | + | |
| 51 | + if (min > k || max < k) | |
| 52 | + return FD_NOSOLUTION; | |
| 53 | + | |
| 54 | + if (min == max) | |
| 55 | + return FD_OK; | |
| 56 | + | |
| 57 | + // XXX: poor man's propagation | |
| 58 | + if (min == k) | |
| 59 | + { | |
| 60 | + for (i = 0; i < terms; ++i) | |
| 61 | + { | |
| 62 | + int c = this->constants[i]; | |
| 63 | + | |
| 64 | + if (c && mins[i] != maxs[i]) | |
| 65 | + { | |
| 66 | + if (c > 0) | |
| 67 | + _fd_var_del_gt(mins[i], VAR(this, i)); | |
| 68 | + else | |
| 69 | + _fd_var_del_lt(maxs[i], VAR(this, i)); | |
| 70 | + | |
| 71 | + // check needed for when variables occur more than once | |
| 72 | + // and with opposite signs | |
| 73 | + if (fd_domain_empty(VAR(this, i))) | |
| 74 | + return FD_NOSOLUTION; | |
| 75 | + | |
| 76 | + _fd_revise_connected(this, VAR(this, i)); | |
| 77 | + } | |
| 78 | + } | |
| 79 | + | |
| 80 | + fd__constraint_set_entailed(this); | |
| 81 | + } | |
| 82 | + else if (max == k) | |
| 83 | + { | |
| 84 | + for (i = 0; i < terms; ++i) | |
| 85 | + { | |
| 86 | + int c = this->constants[i]; | |
| 87 | + | |
| 88 | + if (c && mins[i] != maxs[i]) | |
| 89 | + { | |
| 90 | + if (c > 0) | |
| 91 | + _fd_var_del_lt(maxs[i], VAR(this, i)); | |
| 92 | + else | |
| 93 | + _fd_var_del_gt(mins[i], VAR(this, i)); | |
| 94 | + | |
| 95 | + if (fd_domain_empty(VAR(this, i))) | |
| 96 | + return FD_NOSOLUTION; | |
| 97 | + | |
| 98 | + _fd_revise_connected(this, VAR(this, i)); | |
| 99 | + } | |
| 100 | + } | |
| 101 | + | |
| 102 | + fd__constraint_set_entailed(this); | |
| 103 | + } | |
| 104 | + else | |
| 105 | + { | |
| 106 | + if (max > k) | |
| 107 | + for (i = 0; i < terms; ++i) | |
| 108 | + { | |
| 109 | + int c = this->constants[i]; | |
| 110 | + int xmin = mins[i]; | |
| 111 | + int xmax = maxs[i]; | |
| 112 | + | |
| 113 | + if (c > 0) | |
| 114 | + { | |
| 115 | + // enforce sum{j!=i}(c[j] * min[j]) + c[i] * max[i] <= k | |
| 116 | + | |
| 117 | + if ((xmax - xmin) * c > k - min) | |
| 118 | + { | |
| 119 | + // xmax = floor((k - min) / c) + xmin | |
| 120 | + xmax = (k - min) / c + xmin; | |
| 121 | + | |
| 122 | + _fd_var_del_gt(xmax, VAR(this, i)); | |
| 123 | + | |
| 124 | + if (fd_domain_empty(VAR(this, i))) | |
| 125 | + return FD_NOSOLUTION; | |
| 126 | + | |
| 127 | + _fd_revise_connected(this, VAR(this, i)); | |
| 128 | + } | |
| 129 | + } | |
| 130 | + else if (c < 0) | |
| 131 | + { | |
| 132 | + // enforce sum{j!=i}(c[j] * min[j]) + c[i] * min[i] <= k | |
| 133 | + | |
| 134 | + if ((xmin - xmax) * c > k - min) | |
| 135 | + { | |
| 136 | + // xmin = ceil((k - min) / c) + xmax | |
| 137 | + xmin = (k - min) / c + xmax; | |
| 138 | + | |
| 139 | + _fd_var_del_lt(xmin, VAR(this, i)); | |
| 140 | + | |
| 141 | + if (fd_domain_empty(VAR(this, i))) | |
| 142 | + return FD_NOSOLUTION; | |
| 143 | + | |
| 144 | + _fd_revise_connected(this, VAR(this, i)); | |
| 145 | + } | |
| 146 | + } | |
| 147 | + } | |
| 148 | + | |
| 149 | + if (min < k) | |
| 150 | + for (i = 0; i < terms; ++i) | |
| 151 | + { | |
| 152 | + int c = this->constants[i]; | |
| 153 | + int xmin = mins[i]; | |
| 154 | + int xmax = maxs[i]; | |
| 155 | + | |
| 156 | + if (c > 0) | |
| 157 | + { | |
| 158 | + // enforce sum{j!=i}(c[j] * max[j]) + c[i] * min[i] >= k | |
| 159 | + | |
| 160 | + if ((xmax - xmin) * c > max - k) | |
| 161 | + { | |
| 162 | + // xmin = ceil((k - max) / c) + xmax | |
| 163 | + xmin = (k - max) / c + xmax; | |
| 164 | + | |
| 165 | + _fd_var_del_lt(xmin, VAR(this, i)); | |
| 166 | + | |
| 167 | + if (fd_domain_empty(VAR(this, i))) // domains may have holes | |
| 168 | + return FD_NOSOLUTION; | |
| 169 | + | |
| 170 | + _fd_revise_connected(this, VAR(this, i)); | |
| 171 | + } | |
| 172 | + } | |
| 173 | + else if (c < 0) | |
| 174 | + { | |
| 175 | + // enforce sum{j!=i}(c[j] * max[j]) + c[i] * max[i] >= k | |
| 176 | + | |
| 177 | + if ((xmax - xmin) * c < k - max) | |
| 178 | + { | |
| 179 | + // xmax = floor((k - max) / c) + xmin | |
| 180 | + xmax = (k - max) / c + xmin; | |
| 181 | + | |
| 182 | + _fd_var_del_gt(xmax, VAR(this, i)); | |
| 183 | + | |
| 184 | + if (fd_domain_empty(VAR(this, i))) // domains may have holes | |
| 185 | + return FD_NOSOLUTION; | |
| 186 | + | |
| 187 | + _fd_revise_connected(this, VAR(this, i)); | |
| 188 | + } | |
| 189 | + } | |
| 190 | + } | |
| 191 | + } | |
| 192 | + | |
| 193 | +#ifdef CONSTRAINT_TEMPS | |
| 194 | + // save values | |
| 195 | + *base = min; | |
| 196 | + *(base + 1) = max; | |
| 197 | + | |
| 198 | + fd__constraint_remember(this); | |
| 199 | +#endif | |
| 200 | + | |
| 201 | + return FD_OK; | |
| 202 | +} | |
| 203 | + | |
| 204 | +static int fd_poly_eq_k_propagate2(fd_constraint this, fd_int culprit) | |
| 205 | +{ | |
| 206 | +#ifdef CONSTRAINT_TEMPS | |
| 207 | + int k; | |
| 208 | + int min, max; | |
| 209 | + int terms = this->nvariables; | |
| 210 | + int *mins, *maxs; | |
| 211 | + int i; | |
| 212 | + int *base; | |
| 213 | + int x, c; | |
| 214 | + int nmin, nmin_x, nmax, nmax_x; | |
| 215 | + | |
| 216 | + if (!fd__constraint_data_valid(this)) | |
| 217 | + return fd_poly_eq_k_filter(this); // ignores culprit | |
| 218 | + | |
| 219 | + // bounds filtering | |
| 220 | + | |
| 221 | + base = constraint_memory[this->index]; | |
| 222 | + | |
| 223 | + mins = base + 2; | |
| 224 | + maxs = mins + terms; | |
| 225 | + | |
| 226 | + min = *base; | |
| 227 | + max = *(base + 1); | |
| 228 | + | |
| 229 | + k = this->constants[terms]; | |
| 230 | + | |
| 231 | + // the culprit appears in one of the terms, find out which one(s) | |
| 232 | + for (x = 0; culprit != VAR(this, x); ++x) | |
| 233 | + ; | |
| 234 | + | |
| 235 | + nmin_x = _fd_var_min(VAR(this, x)); | |
| 236 | + nmax_x = _fd_var_max(VAR(this, x)); | |
| 237 | + | |
| 238 | + if (nmin_x == mins[x] && nmax_x == maxs[x]) | |
| 239 | + return FD_OK; | |
| 240 | + | |
| 241 | + nmin = min; | |
| 242 | + nmax = max; | |
| 243 | + | |
| 244 | + do | |
| 245 | + { | |
| 246 | + c = this->constants[x]; | |
| 247 | + | |
| 248 | + if (c > 0) | |
| 249 | + { | |
| 250 | + nmin = nmin + (nmin_x - mins[x]) * c; | |
| 251 | + nmax = nmax - (maxs[x] - nmax_x) * c; | |
| 252 | + } | |
| 253 | + else if (c < 0) | |
| 254 | + { | |
| 255 | + nmin = nmin - (maxs[x] - nmax_x) * c; | |
| 256 | + nmax = nmax + (nmin_x - mins[x]) * c; | |
| 257 | + } | |
| 258 | + | |
| 259 | + if (nmin > k || nmax < k) | |
| 260 | + return FD_NOSOLUTION; | |
| 261 | + | |
| 262 | + mins[x] = nmin_x; | |
| 263 | + maxs[x] = nmax_x; | |
| 264 | + | |
| 265 | + while (++x < terms && culprit != VAR(this, x)) | |
| 266 | + ; | |
| 267 | + } | |
| 268 | + while (x < terms); | |
| 269 | + | |
| 270 | + // XXX: poor man's propagation | |
| 271 | + if (nmin == k) | |
| 272 | + { | |
| 273 | + for (i = 0; i < terms; ++i) | |
| 274 | + { | |
| 275 | + int c = this->constants[i]; | |
| 276 | + | |
| 277 | + if (c && mins[i] != maxs[i]) | |
| 278 | + { | |
| 279 | + if (c > 0) | |
| 280 | + { | |
| 281 | + _fd_var_del_gt(mins[i], VAR(this, i)); | |
| 282 | + | |
| 283 | + maxs[i] = mins[i]; | |
| 284 | + } | |
| 285 | + else | |
| 286 | + { | |
| 287 | + _fd_var_del_lt(maxs[i], VAR(this, i)); | |
| 288 | + | |
| 289 | + mins[i] = maxs[i]; | |
| 290 | + } | |
| 291 | + | |
| 292 | + if (fd_domain_empty(VAR(this, i))) | |
| 293 | + return FD_NOSOLUTION; | |
| 294 | + | |
| 295 | + _fd_revise_connected(this, VAR(this, i)); | |
| 296 | + } | |
| 297 | + } | |
| 298 | + | |
| 299 | + fd__constraint_set_entailed(this); | |
| 300 | + } | |
| 301 | + else if (nmax == k) | |
| 302 | + { | |
| 303 | + for (i = 0; i < terms; ++i) | |
| 304 | + { | |
| 305 | + int c = this->constants[i]; | |
| 306 | + | |
| 307 | + if (c && mins[i] != maxs[i]) | |
| 308 | + { | |
| 309 | + if (c > 0) | |
| 310 | + { | |
| 311 | + _fd_var_del_lt(maxs[i], VAR(this, i)); | |
| 312 | + | |
| 313 | + mins[i] = maxs[i]; | |
| 314 | + } | |
| 315 | + else | |
| 316 | + { | |
| 317 | + _fd_var_del_gt(mins[i], VAR(this, i)); | |
| 318 | + | |
| 319 | + maxs[i] = mins[i]; | |
| 320 | + } | |
| 321 | + | |
| 322 | + if (fd_domain_empty(VAR(this, i))) | |
| 323 | + return FD_NOSOLUTION; | |
| 324 | + | |
| 325 | + _fd_revise_connected(this, VAR(this, i)); | |
| 326 | + } | |
| 327 | + } | |
| 328 | + | |
| 329 | + fd__constraint_set_entailed(this); | |
| 330 | + } | |
| 331 | + | |
| 332 | +#if 0 // this is too expensive | |
| 333 | + { | |
| 334 | + if (nmax < max && nmax > k) | |
| 335 | + for (i = 0; i < terms; ++i) | |
| 336 | + { | |
| 337 | + int c = this->constants[i]; | |
| 338 | + int xmin = mins[i]; | |
| 339 | + int xmax = maxs[i]; | |
| 340 | + | |
| 341 | + if (c > 0) | |
| 342 | + { | |
| 343 | + // enforce sum{j!=i}(c[j] * min[j]) + c[i] * max[i] <= k | |
| 344 | + | |
| 345 | + if ((xmax - xmin) * c > k - nmin) | |
| 346 | + { | |
| 347 | + // xmax = floor((k - nmin) / c) + xmin | |
| 348 | + xmax = (k - nmin) / c + xmin; | |
| 349 | + | |
| 350 | + if (_fd_var_del_gt(xmax, VAR(this, i))) | |
| 351 | + { | |
| 352 | + if (fd_domain_empty(VAR(this, i))) | |
| 353 | + return FD_NOSOLUTION; | |
| 354 | + | |
| 355 | + _fd_revise_connected(this, VAR(this, i)); | |
| 356 | + } | |
| 357 | + | |
| 358 | + maxs[i] = xmax; | |
| 359 | + } | |
| 360 | + } | |
| 361 | + else if (c < 0) | |
| 362 | + { | |
| 363 | + // enforce sum{j!=i}(c[j] * min[j]) + c[i] * min[i] <= k | |
| 364 | + | |
| 365 | + if ((xmin - xmax) * c > k - nmin) | |
| 366 | + { | |
| 367 | + // xmin = ceil((k - nmin) / c) + xmax | |
| 368 | + xmin = (k - nmin) / c + xmax; | |
| 369 | + | |
| 370 | + if (_fd_var_del_lt(xmin, VAR(this, i))) | |
| 371 | + { | |
| 372 | + if (fd_domain_empty(VAR(this, i))) | |
| 373 | + return FD_NOSOLUTION; | |
| 374 | + | |
| 375 | + _fd_revise_connected(this, VAR(this, i)); | |
| 376 | + } | |
| 377 | + | |
| 378 | + mins[i] = xmin; | |
| 379 | + } | |
| 380 | + } | |
| 381 | + } | |
| 382 | + | |
| 383 | + if (nmin > min && nmin < k) | |
| 384 | + for (i = 0; i < terms; ++i) | |
| 385 | + { | |
| 386 | + int c = this->constants[i]; | |
| 387 | + int xmin = mins[i]; | |
| 388 | + int xmax = maxs[i]; | |
| 389 | + | |
| 390 | + if (c > 0) | |
| 391 | + { | |
| 392 | + // enforce sum{j!=i}(c[j] * max[j]) + c[i] * min[i] >= k | |
| 393 | + | |
| 394 | + if ((xmax - xmin) * c > nmax - k) | |
| 395 | + { | |
| 396 | + // xmin = ceil((k - nmax) / c) + xmax | |
| 397 | + xmin = (k - nmax) / c + xmax; | |
| 398 | + | |
| 399 | + if (_fd_var_del_lt(xmin, VAR(this, i))) | |
| 400 | + { | |
| 401 | + | |
| 402 | + if (fd_domain_empty(VAR(this, i))) // domains may have holes | |
| 403 | + return FD_NOSOLUTION; | |
| 404 | + | |
| 405 | + _fd_revise_connected(this, VAR(this, i)); | |
| 406 | + } | |
| 407 | + | |
| 408 | + mins[i] = xmin; | |
| 409 | + } | |
| 410 | + } | |
| 411 | + else if (c < 0) | |
| 412 | + { | |
| 413 | + // enforce sum{j!=i}(c[j] * max[j]) + c[i] * max[i] >= k | |
| 414 | + | |
| 415 | + if ((xmax - xmin) * c < k - nmax) | |
| 416 | + { | |
| 417 | + // xmax = floor((k - nmax) / c) + xmin | |
| 418 | + xmax = (k - nmax) / c + xmin; | |
| 419 | + | |
| 420 | + if (_fd_var_del_gt(xmax, VAR(this, i))) | |
| 421 | + { | |
| 422 | + if (fd_domain_empty(VAR(this, i))) // domains may have holes | |
| 423 | + return FD_NOSOLUTION; | |
| 424 | + | |
| 425 | + _fd_revise_connected(this, VAR(this, i)); | |
| 426 | + } | |
| 427 | + | |
| 428 | + maxs[i] = xmax; | |
| 429 | + } | |
| 430 | + } | |
| 431 | + } | |
| 432 | + } | |
| 433 | +#endif | |
| 434 | + | |
| 435 | + *base = nmin; | |
| 436 | + *(base + 1) = nmax; | |
| 437 | + | |
| 438 | + return FD_OK; | |
| 439 | +#else /* CONSTRAINT_TEMPS */ | |
| 440 | + return fd_poly_eq_k_filter(this); // ignores culprit | |
| 441 | +#endif /* CONSTRAINT_TEMPS */ | |
| 442 | +} | |
| 443 | + | |
| 444 | +fd_constraint fd_poly_eq_k(int cs[], fd_int xs[], int nterms, int k) | |
| 445 | +{ | |
| 446 | + fd_constraint c = _fd_constraint_new(nterms, nterms + 1); | |
| 447 | + int i; | |
| 448 | + | |
| 449 | + if (c) | |
| 450 | + { | |
| 451 | + for (i = 0; i < nterms; ++i) | |
| 452 | + c->variables[i] = FD_INT2C_VAR(xs[i]); | |
| 453 | + for (i = 0; i < nterms; ++i) | |
| 454 | + c->constants[i] = cs[i]; | |
| 455 | + c->constants[nterms] = k; | |
| 456 | +#ifdef CONSTRAINT_CLASS | |
| 457 | + c->kind = FD_CONSTR_POLY_EQ_K; | |
| 458 | +#else /* CONSTRAINT_CLASS */ | |
| 459 | + c->propagator2 = fd_poly_eq_k_propagate2; | |
| 460 | +#endif /* CONSTRAINT_CLASS */ | |
| 461 | + | |
| 462 | + for (i = 0; i < c->nvariables; ++i) | |
| 463 | + _fd_var_add_constraint(VAR(c, i), c); | |
| 464 | + | |
| 465 | + _fd_add_constraint(c); | |
| 466 | + } | |
| 467 | + | |
| 468 | + return c; | |
| 469 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,250 @@ |
| 1 | +/* poly-ne-k(C, X, k) == sum(C . X) != k */ | |
| 2 | + | |
| 3 | +static int fd_poly_ne_k_filter(fd_constraint this) | |
| 4 | +{ | |
| 5 | + int k; | |
| 6 | + int min, max; | |
| 7 | + int terms = this->nvariables; | |
| 8 | + int *mins, *maxs; | |
| 9 | + int unset = 0; // non-singleton variables | |
| 10 | + int i; | |
| 11 | +#ifdef CONSTRAINT_TEMPS | |
| 12 | + int *base; | |
| 13 | + | |
| 14 | + assert(!fd__constraint_data_valid(this)); | |
| 15 | + | |
| 16 | + if (!constraint_memory[this->index]) | |
| 17 | + constraint_memory[this->index] = malloc((2 * terms + 3) * sizeof(int)); | |
| 18 | + | |
| 19 | + base = constraint_memory[this->index]; | |
| 20 | + | |
| 21 | + mins = base + 3; | |
| 22 | + maxs = mins + terms; | |
| 23 | +#else | |
| 24 | + mins = alloca(terms * sizeof(*mins)); | |
| 25 | + maxs = alloca(terms * sizeof(*maxs)); | |
| 26 | +#endif | |
| 27 | + | |
| 28 | + k = this->constants[terms]; | |
| 29 | + | |
| 30 | + // sum the minima and the maxima of the terms | |
| 31 | + min = max = 0; | |
| 32 | + | |
| 33 | + for (i = 0; i < terms; ++i) | |
| 34 | + { | |
| 35 | + int vl, vh; | |
| 36 | + int c = this->constants[i]; | |
| 37 | + | |
| 38 | + if (c > 0) | |
| 39 | + { | |
| 40 | + vl = mins[i] = _fd_var_min(VAR(this, i)); | |
| 41 | + vh = maxs[i] = _fd_var_max(VAR(this, i)); | |
| 42 | + } | |
| 43 | + else | |
| 44 | + { | |
| 45 | + vl = maxs[i] = _fd_var_max(VAR(this, i)); | |
| 46 | + vh = mins[i] = _fd_var_min(VAR(this, i)); | |
| 47 | + } | |
| 48 | + | |
| 49 | + if (c && vl != vh) | |
| 50 | + unset++; | |
| 51 | + | |
| 52 | + min += c * vl; | |
| 53 | + max += c * vh; | |
| 54 | + } | |
| 55 | + | |
| 56 | + if (min > k || max < k) | |
| 57 | + { | |
| 58 | + fd__constraint_set_entailed(this); | |
| 59 | + | |
| 60 | + return FD_OK; | |
| 61 | + } | |
| 62 | + | |
| 63 | + if (min == max) | |
| 64 | + { | |
| 65 | + if (min == k) | |
| 66 | + return FD_NOSOLUTION; | |
| 67 | + | |
| 68 | + fd__constraint_set_entailed(this); | |
| 69 | + | |
| 70 | + return FD_OK; | |
| 71 | + } | |
| 72 | + | |
| 73 | + if (unset == 1) | |
| 74 | + { | |
| 75 | + int m, v; | |
| 76 | + | |
| 77 | + // find out which is the non-singleton variable | |
| 78 | + for (i = terms - 1; i >= 0; --i) | |
| 79 | + if (this->constants[i] && mins[i] != maxs[i]) | |
| 80 | + break; | |
| 81 | + | |
| 82 | + if (this->constants[i] > 0) | |
| 83 | + m = min - this->constants[i] * mins[i]; | |
| 84 | + else | |
| 85 | + m = min - this->constants[i] * maxs[i]; | |
| 86 | + | |
| 87 | + v = (k - m) / this->constants[i]; | |
| 88 | + | |
| 89 | + if (v * this->constants[i] + m == k) | |
| 90 | + fd_update_domain_and_check(del_val, v, VAR(this, i)); | |
| 91 | + | |
| 92 | + fd__constraint_set_entailed(this); | |
| 93 | + } | |
| 94 | + | |
| 95 | +#ifdef CONSTRAINT_TEMPS | |
| 96 | + // save values | |
| 97 | + *base = min; | |
| 98 | + *(base + 1) = max; | |
| 99 | + *(base + 2) = unset; | |
| 100 | + | |
| 101 | + fd__constraint_remember(this); | |
| 102 | +#endif | |
| 103 | + | |
| 104 | + return FD_OK; | |
| 105 | +} | |
| 106 | + | |
| 107 | +static int fd_poly_ne_k_propagate2(fd_constraint this, fd_int culprit) | |
| 108 | +{ | |
| 109 | +#ifdef CONSTRAINT_TEMPS | |
| 110 | + int k; | |
| 111 | + int min, max; | |
| 112 | + int terms = this->nvariables; | |
| 113 | + int *mins, *maxs; | |
| 114 | + int unset; | |
| 115 | + int i; | |
| 116 | + int *base; | |
| 117 | + int x, c; | |
| 118 | + int nmin, nmin_x, nmax, nmax_x; | |
| 119 | + | |
| 120 | + if (!fd__constraint_data_valid(this)) | |
| 121 | + return fd_poly_ne_k_filter(this); // ignores culprit | |
| 122 | + | |
| 123 | + // bounds filtering | |
| 124 | + | |
| 125 | + base = constraint_memory[this->index]; | |
| 126 | + | |
| 127 | + mins = base + 3; | |
| 128 | + maxs = mins + terms; | |
| 129 | + | |
| 130 | + k = this->constants[terms]; | |
| 131 | + | |
| 132 | + min = *base; | |
| 133 | + max = *(base + 1); | |
| 134 | + | |
| 135 | + unset = *(base + 2); | |
| 136 | + | |
| 137 | + // the culprit appears in one of the terms, find out which one(s) | |
| 138 | + for (x = 0; culprit != VAR(this, x); ++x) | |
| 139 | + ; | |
| 140 | + | |
| 141 | + nmin_x = _fd_var_min(VAR(this, x)); | |
| 142 | + nmax_x = _fd_var_max(VAR(this, x)); | |
| 143 | + | |
| 144 | + if (nmin_x == mins[x] && nmax_x == maxs[x]) | |
| 145 | + return FD_OK; | |
| 146 | + | |
| 147 | + nmin = min; | |
| 148 | + nmax = max; | |
| 149 | + | |
| 150 | + do | |
| 151 | + { | |
| 152 | + c = this->constants[x]; | |
| 153 | + | |
| 154 | + if (c && nmin_x == nmax_x) | |
| 155 | + unset--; | |
| 156 | + | |
| 157 | + if (c > 0) | |
| 158 | + { | |
| 159 | + nmin = nmin + (nmin_x - mins[x]) * c; | |
| 160 | + nmax = nmax - (maxs[x] - nmax_x) * c; | |
| 161 | + } | |
| 162 | + else if (c < 0) | |
| 163 | + { | |
| 164 | + nmin = nmin - (maxs[x] - nmax_x) * c; | |
| 165 | + nmax = nmax + (nmin_x - mins[x]) * c; | |
| 166 | + } | |
| 167 | + | |
| 168 | + mins[x] = nmin_x; | |
| 169 | + maxs[x] = nmax_x; | |
| 170 | + | |
| 171 | + while (++x < terms && culprit != VAR(this, x)) | |
| 172 | + ; | |
| 173 | + } | |
| 174 | + while (x < terms); | |
| 175 | + | |
| 176 | + if (nmin > k || nmax < k) | |
| 177 | + { | |
| 178 | + fd__constraint_set_entailed(this); | |
| 179 | + | |
| 180 | + return FD_OK; | |
| 181 | + } | |
| 182 | + | |
| 183 | + if (nmin == nmax) | |
| 184 | + { | |
| 185 | + if (nmin == k) | |
| 186 | + return FD_NOSOLUTION; | |
| 187 | + | |
| 188 | + fd__constraint_set_entailed(this); | |
| 189 | + | |
| 190 | + return FD_OK; | |
| 191 | + } | |
| 192 | + | |
| 193 | + if (unset == 1) | |
| 194 | + { | |
| 195 | + int m, v; | |
| 196 | + | |
| 197 | + // find out which is the non-singleton variable | |
| 198 | + for (i = terms - 1; i >= 0; --i) | |
| 199 | + if (this->constants[i] && mins[i] != maxs[i]) | |
| 200 | + break; | |
| 201 | + | |
| 202 | + if (this->constants[i] > 0) | |
| 203 | + m = min - this->constants[i] * mins[i]; | |
| 204 | + else | |
| 205 | + m = min - this->constants[i] * maxs[i]; | |
| 206 | + | |
| 207 | + v = (k - m) / this->constants[i]; | |
| 208 | + | |
| 209 | + if (v * this->constants[i] + m == k) | |
| 210 | + fd_update_domain_and_check(del_val, v, VAR(this, i)); | |
| 211 | + | |
| 212 | + fd__constraint_set_entailed(this); | |
| 213 | + } | |
| 214 | + | |
| 215 | + *base = nmin; | |
| 216 | + *(base + 1) = nmax; | |
| 217 | + *(base + 2) = unset; | |
| 218 | + | |
| 219 | + return FD_OK; | |
| 220 | +#else /* CONSTRAINT_TEMPS */ | |
| 221 | + return fd_poly_ne_k_filter(this); // ignores culprit | |
| 222 | +#endif /* CONSTRAINT_TEMPS */ | |
| 223 | +} | |
| 224 | + | |
| 225 | +fd_constraint fd_poly_ne_k(int cs[], fd_int xs[], int nterms, int k) | |
| 226 | +{ | |
| 227 | + fd_constraint c = _fd_constraint_new(nterms, nterms + 1); | |
| 228 | + int i; | |
| 229 | + | |
| 230 | + if (c) | |
| 231 | + { | |
| 232 | + for (i = 0; i < nterms; ++i) | |
| 233 | + c->variables[i] = FD_INT2C_VAR(xs[i]); | |
| 234 | + for (i = 0; i < nterms; ++i) | |
| 235 | + c->constants[i] = cs[i]; | |
| 236 | + c->constants[nterms] = k; | |
| 237 | +#ifdef CONSTRAINT_CLASS | |
| 238 | + c->kind = FD_CONSTR_POLY_NE_K; | |
| 239 | +#else /* CONSTRAINT_CLASS */ | |
| 240 | + c->propagator2 = fd_poly_ne_k_propagate2; | |
| 241 | +#endif /* CONSTRAINT_CLASS */ | |
| 242 | + | |
| 243 | + for (i = 0; i < c->nvariables; ++i) | |
| 244 | + _fd_var_add_constraint(VAR(c, i), c); | |
| 245 | + | |
| 246 | + _fd_add_constraint(c); | |
| 247 | + } | |
| 248 | + | |
| 249 | + return c; | |
| 250 | +} | ... | ... |
| ... | ... | @@ -0,0 +1,288 @@ |
| 1 | +/* poly-ne(C, X, y) == sum(C . X) != y */ | |
| 2 | + | |
| 3 | +static int fd_poly_ne_filter(fd_constraint this) | |
| 4 | +{ | |
| 5 | + int ub, lb; | |
| 6 | + int min, max; | |
| 7 | + int terms = this->nconstants; | |
| 8 | + int *mins, *maxs; | |
| 9 | + int unset = 0; // non-singleton variables | |
| 10 | + int i; | |
| 11 | +#ifdef CONSTRAINT_TEMPS | |
| 12 | + int *base; | |
| 13 | + | |
| 14 | + assert(!fd__constraint_data_valid(this)); | |
| 15 | + | |
| 16 | + if (!constraint_memory[this->index]) | |
| 17 | + constraint_memory[this->index] = malloc((2 * terms + 5) * sizeof(int)); | |
| 18 | + | |
| 19 | + base = constraint_memory[this->index]; | |
| 20 | + | |
| 21 | + mins = base + 5; | |
| 22 | + maxs = mins + terms; | |
| 23 | +#else | |
| 24 | + mins = alloca(terms * sizeof(*mins)); | |
| 25 | + maxs = alloca(terms * sizeof(*maxs)); | |
| 26 | +#endif | |
| 27 | + | |
| 28 | + lb = _fd_var_min(VAR(this, this->nvariables - 1)); // lower bound | |
| 29 | + ub = _fd_var_max(VAR(this, this->nvariables - 1)); // upper bound | |
| 30 | + | |
| 31 | + // sum the minima and the maxima of the terms | |
| 32 | + min = max = 0; | |
| 33 | + | |
| 34 | + for (i = 0; i < terms; ++i) | |
| 35 | + { | |
| 36 | + int vl, vh; | |
| 37 | + int c = this->constants[i]; | |
| 38 | + | |
| 39 | + if (c > 0) | |
| 40 | + { | |
| 41 | + vl = mins[i] = _fd_var_min(VAR(this, i)); | |
| 42 | + vh = maxs[i] = _fd_var_max(VAR(this, i)); | |
| 43 | + } | |
| 44 | + else | |
| 45 | + { | |
| 46 | + vl = maxs[i] = _fd_var_max(VAR(this, i)); | |
| 47 | + vh = mins[i] = _fd_var_min(VAR(this, i)); | |
| 48 | + } | |
| 49 | + | |
| 50 | + if (c && vl != vh) | |
| 51 | + unset++; | |
| 52 | + | |
| 53 | + min += c * vl; | |
| 54 | + max += c * vh; | |
| 55 | + } | |
| 56 | + | |
| 57 | + if (min > ub || max < lb) | |
| 58 | + { | |
| 59 | + fd__constraint_set_entailed(this); | |
| 60 | + | |
| 61 | + return FD_OK; | |
| 62 | + } | |
| 63 | + | |
| 64 | + if (min == max) | |
| 65 | + { | |
| 66 | + fd_update_domain_and_check(del_val, min, VAR(this, this->nvariables - 1)); | |
| 67 | + | |
| 68 | + fd__constraint_set_entailed(this); | |
| 69 | + } | |
| 70 | + else if (unset == 1 && lb == ub) | |
| 71 | + { | |
| 72 | + int m, v; | |
| 73 | + | |
| 74 | + // find out which is the non-singleton variable | |
| 75 | + for (i = terms - 1; i >= 0; --i) | |
| 76 | + if (this->constants[i] && mins[i] != maxs[i]) | |
| 77 | + break; | |
| 78 | + | |
| 79 | + if (this->constants[i] > 0) | |
| 80 | + m = min - this->constants[i] * mins[i]; | |
| 81 | + else | |
| 82 | + m = min - this->constants[i] * maxs[i]; | |
| 83 | + | |
| 84 | + v = (lb - m) / this->constants[i]; | |
| 85 | + | |
| 86 | + if (v * this->constants[i] + m == lb) | |
| 87 | + fd_update_domain_and_check(del_val, v, VAR(this, i)); | |
| 88 | + | |
| 89 | + fd__constraint_set_entailed(this); | |
| 90 | + } | |
| 91 | + | |
| 92 | +#ifdef CONSTRAINT_TEMPS | |
| 93 | + // save values | |
| 94 | + *base = lb; | |
| 95 | + *(base + 1) = ub; | |
| 96 | + *(base + 2) = min; | |
| 97 | + *(base + 3) = max; | |
| 98 | + *(base + 4) = unset; | |
| 99 | + | |
| 100 | + fd__constraint_remember(this); | |
| 101 | +#endif | |
| 102 | + | |
| 103 | + return FD_OK; | |
| 104 | +} | |
| 105 | + | |
| 106 | +static int fd_poly_ne_propagate2(fd_constraint this, fd_int culprit) | |
| 107 | +{ | |
| 108 | +#ifdef CONSTRAINT_TEMPS | |
| 109 | + int ub, lb; | |
| 110 | + int min, max; | |
| 111 | + int terms = this->nconstants; | |
| 112 | + int *mins, *maxs; | |
| 113 | + int unset; | |
| 114 | + int i; | |
| 115 | + int *base; | |
| 116 | + int x, c; | |
| 117 | + int nmin, nmin_x, nmax, nmax_x; | |
| 118 | + | |
| 119 | + if (!fd__constraint_data_valid(this)) | |
| 120 | + return fd_poly_ne_filter(this); // ignores culprit | |
| 121 | + | |
| 122 | + // bounds filtering | |
| 123 | + | |
| 124 | + base = constraint_memory[this->index]; | |
| 125 | + | |
| 126 | + mins = base + 5; | |
| 127 | + maxs = mins + terms; | |
| 128 | + | |
| 129 | + lb = *base; | |
| 130 | + ub = *(base + 1); | |
| 131 | + | |
| 132 | + min = *(base + 2); | |
| 133 | + max = *(base + 3); | |
| 134 | + | |
| 135 | + unset = *(base + 4); | |
| 136 | + | |
| 137 | + if (culprit == VAR(this, this->nvariables - 1)) | |
| 138 | + { | |
| 139 | + // the culprit is the sum variable | |
| 140 | + int nlb, nub; | |
| 141 | + | |
| 142 | + nlb = _fd_var_min(culprit); | |
| 143 | + nub = _fd_var_max(culprit); | |
| 144 | + | |
| 145 | + if (nlb == lb && nub == ub) | |
| 146 | + return FD_OK; | |
| 147 | + | |
| 148 | + if (min > nub || max < nlb) | |
| 149 | + fd__constraint_set_entailed(this); | |
| 150 | + | |
| 151 | + if (unset == 1 && nlb == nub) | |
| 152 | + { | |
| 153 | + int m, v; | |
| 154 | + | |
| 155 | + // find out which is the non-singleton variable | |
| 156 | + for (i = this->nvariables - 2; i >= 0; --i) | |
| 157 | + if (this->constants[i] && mins[i] != maxs[i]) | |
| 158 | + break; | |
| 159 | + | |
| 160 | + if (this->constants[i] > 0) | |
| 161 | + m = min - this->constants[i] * mins[i]; | |
| 162 | + else | |
| 163 | + m = min - this->constants[i] * maxs[i]; | |
| 164 | + | |
| 165 | + v = (nlb - m) / this->constants[i]; | |
| 166 | + | |
| 167 | + if (v * this->constants[i] + m == nlb) | |
| 168 | + fd_update_domain_and_check(del_val, v, VAR(this, i)); | |
| 169 | + | |
| 170 | + fd__constraint_set_entailed(this); | |
| 171 | + } | |
| 172 | + | |
| 173 | + *base = nlb; | |
| 174 | + *(base + 1) = nub; | |
| 175 | + | |
| 176 | + return FD_OK; | |
| 177 | + } | |
| 178 | + | |
| 179 | + // the culprit appears in one of the terms, find out which one(s) | |
| 180 | + for (x = 0; culprit != VAR(this, x); ++x) | |
| 181 | + ; | |
| 182 | + | |
| 183 | + nmin_x = _fd_var_min(VAR(this, x)); | |
| 184 | + nmax_x = _fd_var_max(VAR(this, x)); | |
| 185 | + | |
| 186 | + if (nmin_x == mins[x] && nmax_x == maxs[x]) | |
| 187 | + return FD_OK; | |
| 188 | + | |
| 189 | + nmin = min; | |
| 190 | + nmax = max; | |
| 191 | + | |
| 192 | + do | |
| 193 | + { | |
| 194 | + c = this->constants[x]; | |
| 195 | + | |
| 196 | + if (c && nmin_x == nmax_x) | |
| 197 | + unset--; | |
| 198 | + | |
| 199 | + if (c > 0) | |
| 200 | + { | |
| 201 | + nmin = nmin + (nmin_x - mins[x]) * c; | |
| 202 | + nmax = nmax - (maxs[x] - nmax_x) * c; | |
| 203 | + } | |
| 204 | + else if (c < 0) | |
| 205 | + { | |
| 206 | + nmin = nmin - (maxs[x] - nmax_x) * c; | |
| 207 | + nmax = nmax + (nmin_x - mins[x]) * c; | |
| 208 | + } | |
| 209 | + | |
| 210 | + mins[x] = nmin_x; | |
| 211 | + maxs[x] = nmax_x; | |
| 212 | + | |
| 213 | + while (++x < terms && culprit != VAR(this, x)) | |
| 214 | + ; | |
| 215 | + } | |
| 216 | + while (x < terms); | |
| 217 | + | |
| 218 | + if (nmin > ub || nmax < lb) | |
| 219 | + { | |
| 220 | + fd__constraint_set_entailed(this); | |
| 221 | + | |
| 222 | + return FD_OK; | |
| 223 | + } | |
| 224 | + | |
| 225 | + if (nmin == nmax) | |
| 226 | + { | |
| 227 | + fd_update_domain_and_check(del_val, nmin, VAR(this, this->nvariables - 1)); | |
| 228 | + | |
| 229 | + fd__constraint_set_entailed(this); | |
| 230 | + } | |
| 231 | + else if (unset == 1 && lb == ub) | |
| 232 | + { | |
| 233 | + int m, v; | |
| 234 | + | |
| 235 | + // find out which is the non-singleton variable | |
| 236 | + for (i = terms - 1; i >= 0; --i) | |
| 237 | + if (this->constants[i] && mins[i] != maxs[i]) | |
| 238 | + break; | |
| 239 | + | |
| 240 | + if (this->constants[i] > 0) | |
| 241 | + m = min - this->constants[i] * mins[i]; | |
| 242 | + else | |
| 243 | + m = min - this->constants[i] * maxs[i]; | |
| 244 | + | |
| 245 | + v = (lb - m) / this->constants[i]; | |
| 246 | + | |
| 247 | + if (v * this->constants[i] + m == lb) | |
| 248 | + fd_update_domain_and_check(del_val, v, VAR(this, i)); | |
| 249 | + | |
| 250 | + fd__constraint_set_entailed(this); | |
| 251 | + } | |
| 252 | + | |
| 253 | + *(base + 2) = nmin; | |
| 254 | + *(base + 3) = nmax; | |
| 255 | + *(base + 4) = unset; | |
| 256 | + | |
| 257 | + return FD_OK; | |
| 258 | +#else /* CONSTRAINT_TEMPS */ | |
| 259 | + return fd_poly_ne_filter(this); // ignores culprit | |
| 260 | +#endif /* CONSTRAINT_TEMPS */ | |
| 261 | +} | |
| 262 | + | |
| 263 | +fd_constraint fd_poly_ne(int cs[], fd_int xs[], int nterms, fd_int y) | |
| 264 | +{ | |
| 265 | + fd_constraint c = _fd_constraint_new(nterms + 1, nterms); | |
| 266 | + int i; | |
| 267 | + | |
| 268 | + if (c) | |
| 269 | + { | |
| 270 | + for (i = 0; i < nterms; ++i) | |
| 271 | + c->variables[i] = FD_INT2C_VAR(xs[i]); | |
| 272 | + c->variables[nterms] = FD_INT2C_VAR(y); | |
| 273 | + for (i = 0; i < nterms; ++i) | |
| 274 | + c->constants[i] = cs[i]; | |
| 275 | +#ifdef CONSTRAINT_CLASS | |
| 276 | + c->kind = FD_CONSTR_POLY_NE; | |
| 277 | +#else /* CONSTRAINT_CLASS */ | |
| 278 | + c->propagator2 = fd_poly_ne_propagate2; | |
| 279 | +#endif /* CONSTRAINT_CLASS */ | |
| 280 | + | |
| 281 | + for (i = 0; i < c->nvariables; ++i) | |
| 282 | + _fd_var_add_constraint(VAR(c, i), c); | |
| 283 | + | |
| 284 | + _fd_add_constraint(c); | |
| 285 | + } | |
| 286 | + | |
| 287 | + return c; | |
| 288 | +} | ... | ... |