/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Patrick Pekczynski * * Copyright: * Patrick Pekczynski, 2004 * * Last modified: * $Date: 2007-08-09 15:30:21 +0200 (Thu, 09 Aug 2007) $ by $Author: tack $ * $Revision: 4790 $ * * This file is part of Gecode, the generic constraint * development environment: * http://www.gecode.org * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ namespace Gecode { namespace Int { namespace GCC { /** * \brief Lower Bounds constraint (LBC) stating * \f$ \forall j \in \{0, \dots, |k|-1\}: * \#\{i\in\{0, \dots, |x| - 1\} | x_i = card(k_j)\} \geq min(k_j)\f$ * Hence the lbc constraints the variables such that every value occurs * at least as often as specified by its lower cardinality bound. * \param home current space * \param x the problem variables * \param nb denotes number of unique bounds * \param hall contains information about the hall structure of the problem * (cf. HallInfo) * \param rank ranking information about the variable bounds (cf. Rank) * \param lps partial sum structure for the lower cardinality bounds (cf. PartialSum) * \param mu permutation \f$ \mu \f$ such that * \f$ \forall i\in \{0, \dots, |x|-2\}: * max(x_{\mu(i)}) \leq max(x_{\mu(i+1)})\f$ * \param nu permutation \f$ \nu \f$ such that * \f$ \forall i\in \{0, \dots, |x|-2\}: * min(x_{\mu(i)}) \leq min(x_{\mu(i+1)})\f$ */ template inline ExecStatus lbc(Space* home, ViewArray& x, int& nb, HallInfo hall[], Rank rank[], PartialSum* lps, int mu[], int nu[]){ ExecStatus es = ES_FIX; int n = x.size(); /* * Let I(S) denote the number of variables whose domain intersects * the set S and C(S) the number of variables whose domain is containded * in S. Let further min_cap(S) be the minimal number of variables * that must be assigned to values, that is * min_cap(S) is the sum over all l[i] for a value v_i that is an * element of S. * * A failure set is a set F if * I(F) < min_cap(F) * An unstable set is a set U if * I(U) = min_cap(U) * A stable set is a set S if * C(S) > min_cap(S) and S intersetcs nor * any failure set nor any unstable set * forall unstable and failure sets * * failure sets determine the satisfiability of the LBC * unstable sets have to be pruned * stable set do not have to be pruned * * hall[].ps ~ stores the unstable * sets that have to be pruned * hall[].s ~ stores sets that must not be pruned * hall[].h ~ contains stable and unstable sets * hall[].d ~ contains the difference between interval bounds, i.e. * the minimal capacity of the interval * hall[].t ~ contains the critical capacity pointer, pointing to the * values */ // LBC lower bounds int i = 0; int j = 0; int w = 0; int z = 0; int v = 0; //initialization of the tree structure int rightmost = nb + 1; // rightmost accesible value in bounds int bsize = nb + 2; w = rightmost; // test // unused but uninitialized hall[0].d = 0; hall[0].s = 0; hall[0].ps = 0; for (i = bsize; --i; ) { // i must not be zero int pred = i - 1; hall[i].s = pred; hall[i].ps = pred; hall[i].d = lps->sumup(hall[pred].bounds, hall[i].bounds - 1); /* Let [hall[i].bounds,hall[i-1].bounds]=:I * If the capacity is zero => min_cap(I) = 0 * => I cannot be a failure set * => I is an unstable set */ if (hall[i].d == 0) { hall[pred].h = w; } else { hall[w].h = pred; w = pred; } } w = rightmost; for (i = bsize; i--; ) { // i can be zero hall[i].t = i - 1; if (hall[i].d == 0) { hall[i].t = w; } else { hall[w].t = i; w = i; } } /* * The algorithm assigns to each value v in bounds * empty buckets corresponding to the minimal capacity l[i] to be * filled for v. (the buckets correspond to hall[].d containing the * difference between the interval bounds) Processing it * searches for the smallest value v in dom(x_i) that has an * empty bucket, i.e. if all buckets are filled it is guaranteed * that there are at least l[i] variables that will be * instantiated to v. Since the buckets are initially empty, * they are considered as FAILURE SETS */ for (i = 0; i < n; i++) { // visit intervals in increasing max order int x0 = rank[mu[i]].min; int y = rank[mu[i]].max; int succ = x0 + 1; z = pathmax_t(hall, succ); j = hall[z].t; /* * POTENTIALLY STABLE SET: * z \neq succ \Leftrigharrow z>succ, i.e. * min(D_{\mu(i)}) is guaranteed to occur min(K_i) times * \Rightarrow [x0, min(y,z)] is potentially stable */ if (z != succ) { w = pathmax_ps(hall, succ); v = hall[w].ps; pathset_ps(hall, succ, w, w); w = std::min(y, z); pathset_ps(hall, hall[w].ps, v, w); hall[w].ps = v; } /* * STABLE SET: * being stable implies being potentially stable, i.e. * [hall[y].ps, hall[y].bounds-1] is the largest stable subset of * [hall[j].bounds, hall[y].bounds-1]. */ if (hall[z].d <= lps->sumup(hall[y].bounds, hall[z].bounds - 1)) { w = pathmax_s(hall, hall[y].ps); pathset_s(hall, hall[y].ps, w, w); // Path compression v = hall[w].s; pathset_s(hall, hall[y].s, v, y); hall[y].s = v; } else { /* * FAILURE SET: * If the considered interval [x0,y] is neither POTENTIALLY STABLE * nor STABLE there are still buckets that can be filled, * therefore d can be decreased. If d equals zero the intervals * minimum capacity is met and thepath can be compressed to the * next value having an empty bucket. * see DOMINATION in "gcc/ubc.icc" */ if (--hall[z].d == 0) { hall[z].t = z + 1; z = pathmax_t(hall, hall[z].t); hall[z].t = j; } /* * FINDING NEW LOWER BOUND: * If the lower bound belongs to an unstable or a stable set, * remind the new value we might assigned to the lower bound * in case the variable doesn't belong to a stable set. */ if (hall[x0].h > x0) { hall[i].newBound = pathmax_h(hall, x0); w = hall[i].newBound; pathset_h(hall, x0, w, w); // path compression } else { // Do not shrink the variable: take old min as new min hall[i].newBound = x0; } /* UNSTABLE SET * If an unstable set is discovered * the difference between the interval bounds is equal to the * number of variables whose domain intersect the interval * (see ZEROTEST in "gcc/ubc.icc") */ // CLEARLY THIS WAS NOT STABLE == UNSTABLE if (hall[z].d == lps->sumup(hall[y].bounds, hall[z].bounds - 1)) { if (hall[y].h > y) /* * y is not the end of the potentially stable set * thus ensure that the potentially stable superset is marked */ y = hall[y].h; // Equivalent to pathmax since the path is fully compressed int predj = j - 1; pathset_h(hall, hall[y].h, predj, y); // mark the new unstable set [j,y] hall[y].h = predj; } } pathset_t(hall, succ, z, z); // path compression } /* If there is a FAILURE SET left the minimum occurences of the values * are not guaranteed. In order to satisfy the LBC the last value * in the stable and unstable datastructure hall[].h must point to * the sentinel at the beginning of bounds. */ if (hall[nb].h != 0) { return ES_FAILED; // no solution } // Perform path compression over all elements in // the stable interval data structure. This data // structure will no longer be modified and will be // accessed n or 2n times. Therefore, we can afford // a linear time compression. for (i = bsize; --i;) { if (hall[i].s > i) { hall[i].s = w; } else { w = i; } } /* * UPDATING LOWER BOUND: * For all variables that are not a subset of a stable set, * shrink the lower bound, i.e. forall stable sets S we have: * x0 < S_min <= y <=S_max or S_min <= x0 <= S_max < y * that is [x0,y] is NOT a proper subset of any stable set S */ for (i = n; i--; ) { int x0 = rank[mu[i]].min; int y = rank[mu[i]].max; // update only those variables that are not contained in a stable set if ((hall[x0].s <= x0) || (y > hall[x0].s)) { //still have to check this out, how skipping works (consider dominated indices) int m = lps->skipNonNullElementsRight(hall[hall[i].newBound].bounds); ModEvent me = x[mu[i]].gq(home, m); GECODE_ME_CHECK(me); if (me_modified(me) && m != x[mu[i]].min()) { es = ES_NOFIX; } if (shared && me_modified(me)) { es = ES_NOFIX; } } } //LBC narrow upper bounds w = 0; for (i = 0; i <= nb; i++) { hall[i].d = lps->sumup(hall[i].bounds, hall[i + 1].bounds - 1); if (hall[i].d == 0) { hall[i].t = w; } else { hall[w].t = i; w = i; } } hall[w].t = i; w = 0; for (i = 1; i <= nb; i++) { if (hall[i - 1].d == 0) { hall[i].h = w; } else { hall[w].h = i; w = i; } } hall[w].h = i; for (i = n; i--; ) { // visit intervals in decreasing min order // i.e. minsorted from right to left int x0 = rank[nu[i]].max; int y = rank[nu[i]].min; int pred = x0 - 1; // predecessor of x0 in the indices z = pathmin_t(hall, pred); j = hall[z].t; /* If the variable is not in a discovered stable set * (see above condition for STABLE SET) */ if (hall[z].d > lps->sumup(hall[z].bounds, hall[y].bounds - 1)) { //FAILURE SET if (--hall[z].d == 0) { hall[z].t = z - 1; z = pathmin_t(hall, hall[z].t); hall[z].t = j; } //FINDING NEW UPPER BOUND if (hall[x0].h < x0) { w = pathmin_h(hall, hall[x0].h); hall[i].newBound = w; pathset_h(hall, x0, w, w); // path compression } else { hall[i].newBound = x0; } //UNSTABLE SET if (hall[z].d == lps->sumup(hall[z].bounds, hall[y].bounds - 1)) { if (hall[y].h < y) { y = hall[y].h; } int succj = j + 1; //mark new unstable set [y,j] pathset_h(hall, hall[y].h, succj, y); hall[y].h = succj; } } pathset_t(hall, pred, z, z); } // UPDATING UPPER BOUND for (i = n; i--; ) { int x0 = rank[nu[i]].min; int y = rank[nu[i]].max; if ((hall[x0].s <= x0) || (y > hall[x0].s)){ int m = lps->skipNonNullElementsLeft(hall[hall[i].newBound].bounds - 1); ModEvent me = x[nu[i]].lq(home, m); GECODE_ME_CHECK(me); if (me_modified(me) && m != x[nu[i]].max()) { es = ES_NOFIX; } if (shared && me_modified(me)) { es = ES_NOFIX; } } } return es; } }}} // STATISTICS: int-prop