diff --git a/changelog.in b/changelog.in index 900229276c..d44fc9620a 100755 --- a/changelog.in +++ b/changelog.in @@ -257,6 +257,17 @@ The new fault-injection tests exercise clone, dispose-notice, IntSet, MiniModel, and heap-allocation failure paths that are otherwise hard to reproduce reliably. +[ENTRY] +Module: int +What: performance +Rank: minor +Issue: 202 +Thanks: Fabio Tardivo +[DESCRIPTION] +Strengthen bin-packing propagation with lower bounds based on +dual-feasible functions, improving early detection of infeasible +packing states. + [ENTRY] Module: other What: change diff --git a/gecode/driver/options.cpp b/gecode/driver/options.cpp index bf03411a10..5c6c1fbf87 100755 --- a/gecode/driver/options.cpp +++ b/gecode/driver/options.cpp @@ -329,6 +329,7 @@ namespace Gecode { else if (!strncmp("dom",a,e)) { b = IPL_DOM; } else if (!strncmp("basic",a,e)) { m |= IPL_BASIC; } else if (!strncmp("advanced",a,e)) { m |= IPL_ADVANCED; } + else if (!strncmp("full",a,e)) { m |= IPL_FULL; } else { std::cerr << "Wrong argument \"" << a << "\" for option \"" << iopt << "\"" @@ -350,7 +351,7 @@ namespace Gecode { IplOption::help(void) { using namespace std; cerr << '\t' << iopt - << " (def,val,bnd,dom,basic,advanced)" << endl + << " (def,val,bnd,dom,basic,advanced,full)" << endl << "\t\tdefault: "; switch (vbd(cur)) { case IPL_DEF: cerr << "def"; break; @@ -359,8 +360,12 @@ namespace Gecode { case IPL_DOM: cerr << "dom"; break; default: GECODE_NEVER; } - if (cur & IPL_BASIC) cerr << ",basic"; - if (cur & IPL_ADVANCED) cerr << ",advanced"; + if ((cur & IPL_FULL) == IPL_FULL) + cerr << ",full"; + else { + if (cur & IPL_BASIC) cerr << ",basic"; + if (cur & IPL_ADVANCED) cerr << ",advanced"; + } cerr << endl << "\t\t" << exp << endl; } diff --git a/gecode/int.hh b/gecode/int.hh index 301eb0ddc4..05769055f6 100755 --- a/gecode/int.hh +++ b/gecode/int.hh @@ -1015,6 +1015,7 @@ namespace Gecode { IPL_BASIC = 4, ///< Use basic propagation algorithm IPL_ADVANCED = 8, ///< Use advanced propagation algorithm IPL_BASIC_ADVANCED = IPL_BASIC | IPL_ADVANCED, ///< Use both + IPL_FULL = IPL_BASIC_ADVANCED, ///< Use full propagation IPL_BITS_ = 4 ///< Number of bits required (internal) }; @@ -3147,7 +3148,16 @@ namespace Gecode { * for each \f$i\f$ with \f$0\leq i<|b|\f$ the constraint * \f$0\leq b_i<|l|\f$ holds. * - * The propagation follows: Paul Shaw. A Constraint for Bin Packing. CP 2004. + * Basic and knapsack propagation are based on: + * Paul Shaw. A Constraint for Bin Packing. CP 2004. + * The lower-bound phase uses dual-feasible functions described in: + * Tardivo et al. CP for Bin Packing with Multi-Core and GPUs. CP 2024. + * The propagation level \a ipl controls the amount of filtering: + * - \a IPL_BASIC performs basic load and item filtering only. + * - \a IPL_ADVANCED performs basic filtering, knapsack filtering using + * NoSum, and a small DFF portfolio (CCM1 and MT). It is the default + * (\a IPL_DEF). + * - \a IPL_FULL performs all of the above and the complete DFF portfolio. * * Throws the following exceptions: * - Of type Int::ArgumentSizeMismatch if \a b and \a s are not of @@ -3191,6 +3201,14 @@ namespace Gecode { * number of items due to the Bron-Kerbosch algorithm used for finding * the maximal conflict item sets. * + * The propagation level \a ipl controls the filtering performed by each + * per-dimension bin-packing propagator: + * - \a IPL_BASIC performs basic load and item filtering only. + * - \a IPL_ADVANCED performs basic filtering, knapsack filtering using + * NoSum, and a small DFF portfolio (CCM1 and MT). It is the default + * (\a IPL_DEF). + * - \a IPL_FULL performs all of the above and the complete DFF portfolio. + * * Throws the following exceptions: * - Of type Int::ArgumentSizeMismatch if any of the following properties * is violated: \f$|b|=n\f$, \f$|l|=m\cdot d\f$, \f$|s|=n\cdot d\f$, diff --git a/gecode/int/bin-packing.cpp b/gecode/int/bin-packing.cpp index c9776e6999..9a5844299f 100755 --- a/gecode/int/bin-packing.cpp +++ b/gecode/int/bin-packing.cpp @@ -41,7 +41,7 @@ namespace Gecode { binpacking(Home home, const IntVarArgs& l, const IntVarArgs& b, const IntArgs& s, - IntPropLevel) { + IntPropLevel ipl) { using namespace Int; if (same(l,b)) throw ArgumentSame("Int::binpacking"); @@ -59,14 +59,14 @@ namespace Gecode { for (int i=0; i * * Contributing authors: + * Fabio Tardivo * Stefano Gualandi * * Copyright: + * Fabio Tardivo, 2024 * Stefano Gualandi, 2013 * Christian Schulte, 2010 * @@ -127,13 +129,21 @@ namespace Gecode { namespace Int { namespace BinPacking { int operator [](int i) const; }; + /// Range of lambda values + struct LambdaRange { + int min; + int max; + }; /** * \brief Bin-packing propagator * - * The algorithm is taken from: + * Basic and knapsack propagation are based on: * Paul Shaw. A Constraint for Bin Packing. CP 2004. * + * The lower-bound phase uses dual-feasible functions described in: + * Tardivo et al. CP for Bin Packing with Multi-Core and GPUs. CP 2024. + * * Requires \code #include \endcode * * \ingroup FuncIntProp @@ -144,17 +154,21 @@ namespace Gecode { namespace Int { namespace BinPacking { ViewArray l; /// Items with bin and size ViewArray bs; + /// Propagation level + IntPropLevel ipl; /// Total size of all items int t; /// Constructor for posting - Pack(Home home, ViewArray& l, ViewArray& bs); + Pack(Home home, ViewArray& l, ViewArray& bs, + IntPropLevel ipl); /// Constructor for cloning \a p Pack(Space& home, Pack& p); public: /// Post propagator for loads \a l and items \a bs GECODE_INT_EXPORT static ExecStatus post(Home home, - ViewArray& l, ViewArray& bs); + ViewArray& l, ViewArray& bs, + IntPropLevel ipl=IPL_DEF); /// Detect non-existence of sums in \a a .. \a b template bool nosum(const SizeSet& s, int a, int b, int& ap, int& bp); @@ -175,6 +189,40 @@ namespace Gecode { namespace Int { namespace BinPacking { virtual Actor* copy(Space& home); /// Destructor virtual size_t dispose(Space& home); + /// Reductions + static int const n_reductions = 3; + static void calc_reductions(const ViewArray& bs, + const ViewArray& l, + int* weights_base_reduction, + int& capacity_base_reduction, + int* delta_reductions); + /// Dual Feasible Functions + static int f_ccm1(int w, int l, int c); + static int f_mt(int w, int l, int c); + static int f_bj1(int w, int l, int c); + static int f_vb2_base(int w, int l, int c); + static int f_vb2(int w, int l, int c); + static int f_fs1(int w, int l, int c); + static int f_rad2_base(int w, int l, int c); + static int f_rad2(int w, int l, int c); + static int const n_lambda_samples = 256; + static LambdaRange l_ccm1(int c); + static LambdaRange l_mt(int c); + static LambdaRange l_bj1(int c); + static LambdaRange l_vb2(int c); + static LambdaRange l_fs1(int c); + static LambdaRange l_rad2(int c); + static LambdaRange sanitize_lambda_range(LambdaRange lambda, + int n_weights, int max_weight); + /// Lower bound + template + static int calc_dff_lower_bound_single_lambda(const int* weights, + int n_weights, + int capacity, int lambda); + template + static int calc_dff_lower_bound(const int* weights, int n_weights, + int capacity, int n_not_zero_weights, + int max_weight, bool sanitize = false); }; diff --git a/gecode/int/bin-packing/propagate.cpp b/gecode/int/bin-packing/propagate.cpp index 9425fdd460..f22ce2cd11 100755 --- a/gecode/int/bin-packing/propagate.cpp +++ b/gecode/int/bin-packing/propagate.cpp @@ -3,7 +3,11 @@ * Main authors: * Christian Schulte * + * Contributing authors: + * Fabio Tardivo + * * Copyright: + * Fabio Tardivo, 2024 * Christian Schulte, 2010 * * This file is part of Gecode, the generic constraint @@ -209,6 +213,10 @@ namespace Gecode { namespace Int { namespace BinPacking { region.free(); } + // Stop after basic propagation unless stronger filtering is requested. + if (ba(ipl) == IPL_BASIC) + return ES_NOFIX; + // Only if the propagator is at fixpoint here, continue with the more // expensive stage for propagation. if (IntView::me(modeventdelta()) != ME_INT_NONE) @@ -278,77 +286,90 @@ namespace Gecode { namespace Int { namespace BinPacking { // Perform lower bound checking if (n > 0) { - // Find capacity estimate (we start from bs[0] as it might be - // not packable, actually (will be detected later anyway)! - int c = bs[0].size(); - for (int j=0; j(c+1); - - for (int i=0; i l[j].max()) { - n_s[c - l[j].max()]++; nm++; - } - - // Sizes of items and remaining bin loads - int* s = region.alloc(nm); - - // Setup sorted sizes { - int k=0; - for (int i=c+1; i--; ) - for (int j=n_s[i]; j--; ) - s[k++]=i; - assert(k == nm); - } - - // Items in N1 are from 0 ... n1 - 1 - int n1 = 0; - // Items in N2 are from n1 ... n12 - 1, we count elements in N1 and N2 - int n12 = 0; - // Items in N3 are from n12 ... n3 - 1 - int n3 = 0; - // Free space in N2 - int f2 = 0; - // Total size of items in N3 - int s3 = 0; - - // Initialize n12 and f2 - for (; (n12 < nm) && (s[n12] > c/2); n12++) - f2 += c - s[n12]; - - // Initialize n3 and s3 - for (n3 = n12; n3 < nm; n3++) - s3 += s[n3]; - - // Compute lower bounds - for (int k=0; k<=c/2; k++) { - // Make N1 larger by adding elements and N2 smaller - for (; (n1 < nm) && (s[n1] > c-k); n1++) - f2 -= c - s[n1]; - assert(n1 <= n12); - // Make N3 smaller by removing elements - for (; (s[n3-1] < k) && (n3 > n12); n3--) - s3 -= s[n3-1]; - // Overspill - int o = (s3 > f2) ? ((s3 - f2 + c - 1) / c) : 0; - if (n12 + o > m) - return ES_FAILED; + // Allocate auxiliary data + int n_bins = l.size(); + int n_weights_reductions = n_bins + bs.size(); + int* weights_base_reduction = + region.alloc(n_weights_reductions); + int* weights_current_reduction = + region.alloc(n_weights_reductions); + int* delta_reductions = region.alloc(n_reductions); + bool full = ba(ipl) != IPL_ADVANCED; + + // Initialize reductions + int capacity_base_reduction = 0; + calc_reductions(bs, l, weights_base_reduction, + capacity_base_reduction, delta_reductions); + + for (int r_idx = 0; r_idx < n_reductions; r_idx += 1) { + // Calculate reduction parameters + int& delta = delta_reductions[r_idx]; + long long int capacity_ll = + static_cast(capacity_base_reduction) + delta; + + if ((capacity_ll > 0) && + (capacity_ll <= std::numeric_limits::max())) { + int capacity = static_cast(capacity_ll); + // Calculate reduction + int n_not_zero_weights = 0; + int max_weight = 0; + int* weights = weights_current_reduction; + for (int w_idx = 0; w_idx < n_weights_reductions; w_idx += 1) { + int weight = + weights_base_reduction[w_idx] + (w_idx < n_bins ? delta : 0); + n_not_zero_weights += weight != 0; + max_weight = std::max(max_weight, weight); + weights[w_idx] = weight; + } + + // Check the small DFF portfolio used by advanced propagation. + int lower_bound = + calc_dff_lower_bound + (weights, n_weights_reductions, capacity, + n_not_zero_weights, max_weight); + if (lower_bound > n_bins) + return ES_FAILED; + + lower_bound = + calc_dff_lower_bound + (weights, n_weights_reductions, capacity, + n_not_zero_weights, max_weight); + if (lower_bound > n_bins) + return ES_FAILED; + + if (full) { + // Check the remaining DFFs for full propagation. + lower_bound = + calc_dff_lower_bound + (weights, n_weights_reductions, capacity, + n_not_zero_weights, max_weight); + if (lower_bound > n_bins) + return ES_FAILED; + + lower_bound = + calc_dff_lower_bound + (weights, n_weights_reductions, capacity, + n_not_zero_weights, max_weight, true); + if (lower_bound > n_bins) + return ES_FAILED; + + lower_bound = + calc_dff_lower_bound + (weights, n_weights_reductions, capacity, + n_not_zero_weights, max_weight, true); + if (lower_bound > n_bins) + return ES_FAILED; + + lower_bound = + calc_dff_lower_bound + (weights, n_weights_reductions, capacity, + n_not_zero_weights, max_weight); + if (lower_bound > n_bins) + return ES_FAILED; + } + } + } } region.free(); } @@ -357,7 +378,8 @@ namespace Gecode { namespace Int { namespace BinPacking { } ExecStatus - Pack::post(Home home, ViewArray& l, ViewArray& bs) { + Pack::post(Home home, ViewArray& l, ViewArray& bs, + IntPropLevel ipl) { // Sort according to size Support::quicksort(&bs[0], bs.size()); // Total size of items @@ -389,12 +411,54 @@ namespace Gecode { namespace Int { namespace BinPacking { GECODE_ME_CHECK(l[j].gq(home,0)); GECODE_ME_CHECK(l[j].lq(home,s)); } - (void) new (home) Pack(home,l,bs); + (void) new (home) Pack(home,l,bs,ipl); return ES_OK; } } + void + Pack::calc_reductions(const ViewArray& bs, + const ViewArray& l, + int* weights_base_reduction, + int& capacity_base_reduction, + int* delta_reductions) { + // Reset values + int n_weights_reductions = l.size() + bs.size(); + for (int w_idx = 0; w_idx < n_weights_reductions; w_idx += 1) + weights_base_reduction[w_idx] = 0; + + // R0 + int n_bins = l.size(); + int n_items = bs.size(); + capacity_base_reduction = 0; + for (int b_idx = 0; b_idx < n_bins; b_idx += 1) + capacity_base_reduction = + std::max(capacity_base_reduction, l[b_idx].max()); + for (int b_idx = 0; b_idx < n_bins; b_idx += 1) + weights_base_reduction[b_idx] = + capacity_base_reduction - l[b_idx].max(); + for (int i_idx = 0; i_idx < n_items; i_idx += 1) { + bool i_assigned = bs[i_idx].bin().assigned(); + int i_weight = bs[i_idx].size(); + if (i_assigned) { + int b_idx = bs[i_idx].bin().val(); + weights_base_reduction[b_idx] += i_weight; + } else { + weights_base_reduction[n_bins + i_idx] = i_weight; + } + } + + // RMin, RMax + int smallest_virtual_weight = std::numeric_limits::max(); + for (int b_idx = 0; b_idx < n_bins; b_idx += 1) + smallest_virtual_weight = + std::min(smallest_virtual_weight, weights_base_reduction[b_idx]); + delta_reductions[0] = -smallest_virtual_weight; + delta_reductions[1] = 0; + delta_reductions[2] = capacity_base_reduction - + smallest_virtual_weight - smallest_virtual_weight + 1; + } + }}} // STATISTICS: int-prop - diff --git a/gecode/int/bin-packing/propagate.hpp b/gecode/int/bin-packing/propagate.hpp index 36241a3a0d..9cf3d4cdd5 100755 --- a/gecode/int/bin-packing/propagate.hpp +++ b/gecode/int/bin-packing/propagate.hpp @@ -3,7 +3,11 @@ * Main authors: * Christian Schulte * + * Contributing authors: + * Fabio Tardivo + * * Copyright: + * Fabio Tardivo, 2024 * Christian Schulte, 2010 * * This file is part of Gecode, the generic constraint @@ -147,8 +151,10 @@ namespace Gecode { namespace Int { namespace BinPacking { */ forceinline - Pack::Pack(Home home, ViewArray& l0, ViewArray& bs0) - : Propagator(home), l(l0), bs(bs0), t(0) { + Pack::Pack(Home home, ViewArray& l0, ViewArray& bs0, + IntPropLevel ipl0) + : Propagator(home), l(l0), bs(bs0), + ipl(ba(ipl0) == IPL_DEF ? IPL_ADVANCED : ipl0), t(0) { l.subscribe(home,*this,PC_INT_BND); bs.subscribe(home,*this,PC_INT_DOM); for (int i=0; i c - w; // x > c / 2 + int const c1 = w == c - w; // x == c / 2 + int const c2 = w < c - w; // x < c / 2 + + // Values + int const v0 = 2 * ((c / l) - ((c - w) / l)); + int const v1 = c / l; + int const v2 = 2 * (w / l); + + return (c0 * v0) + (c1 * v1) + (c2 * v2); + } + + forceinline int + Pack::f_mt(int w, int l, int c) { + // Conditions + int const c0 = w < l; + int const c1 = (l <= w) && (w <= c - l); + int const c2 = c - l < w; + + // Values + int const v0 = 0; + int const v1 = w; + int const v2 = c; + + return (c0 * v0) + (c1 * v1) + (c2 * v2); + } + + forceinline int + Pack::f_bj1(int w, int l, int c) { + // Auxiliary values + int const p = l - (c % l); + + // Conditions + int const c0 = w % l <= c % l; + int const c1 = w % l > c % l; + + // Values + int const v0 = (w / l) * p; + int const v1 = (w / l) * p + (w % l) - (c % l); + + return (c0 * v0) + (c1 * v1); + } + + forceinline int + Pack::f_vb2_base(int w, int l, int c) { + int v = ceil_div_pp(l * w, c); + return v > 0 ? v - 1 : 0; + } + + forceinline int + Pack::f_vb2(int w, int l, int c) { + int const c0 = w > c - w; + int const c1 = w == c - w; + int const c2 = w < c - w; + + int const t0 = f_vb2_base(c, l, c); + int const t1 = f_vb2_base(w, l, c); + int const t2 = f_vb2_base(c - w, l, c); + + int const v0 = 2 * t0 - 2 * t2; + int const v1 = t0; + int const v2 = 2 * t1; + + return (c0 * v0) + (c1 * v1) + (c2 * v2); + } + + forceinline int + Pack::f_fs1(int w, int l, int c) { + // Conditions + int const c0 = w * (l + 1) % c == 0; + int const c1 = w * (l + 1) % c != 0; + + // Values + int const v0 = w * l; + int const v1 = ((w * (l + 1)) / c) * c; + + return (c0 * v0) + (c1 * v1); + } + + forceinline int + Pack::f_rad2_base(int w, int l, int c) { + // Conditions + int const c0 = w < l; + int const c1 = (l <= w) && (w <= c - 2 * l); + int const c2 = (c - 2 * l < w) && (w < 2 * l); + + // Values + int const v0 = 0; + int const v1 = c / 3; + int const v2 = c / 2; + + return (c0 * v0) + (c1 * v1) + (c2 * v2); + } + + forceinline int + Pack::f_rad2(int w, int l, int c) { + // Conditions + int const c0 = w < 2 * l; + int const c1 = 2 * l <= w; + + // Values + int const v0 = f_rad2_base(w, l, c); + int const v1 = c - f_rad2_base(c - w, l, c); + + return (c0 * v0) + (c1 * v1); + } + + forceinline LambdaRange + Pack::l_ccm1(int c) { + return {1, c / 2}; + } + + forceinline LambdaRange + Pack::l_mt(int c) { + return {0, c / 2}; + } + + forceinline LambdaRange + Pack::l_bj1(int c) { + return {1, c}; + } + + forceinline LambdaRange + Pack::l_vb2(int c) { + return {2, c}; + } + + forceinline LambdaRange + Pack::l_fs1(int) { + return {1, 100}; + } + + forceinline LambdaRange + Pack::l_rad2(int c) { + return {c / 4 + 1, c / 3}; + } + + forceinline LambdaRange + Pack::sanitize_lambda_range(LambdaRange lambda_range, + int n_not_zero_weights, int max_weight) { + long long int n = static_cast(n_not_zero_weights) * + static_cast(max_weight); + if (n != 0) { + int l_max = + std::min(static_cast(std::numeric_limits::max() / n), + lambda_range.max); + return {lambda_range.min, l_max}; + } else { + return {0, -1}; + } + } + + template + forceinline int + Pack::calc_dff_lower_bound_single_lambda(const int* weights, int n_weights, + int capacity, int lambda) { + // Transform and sum the weights + long long int sum_transformed_weights = 0; + for (int w_idx = 0; w_idx < n_weights; w_idx += 1) + sum_transformed_weights += f(weights[w_idx], lambda, capacity); + + // Transform the capacity + int transformed_capacity = f(capacity, lambda, capacity); + + // Lower bound + return static_cast + (ceil_div_pp(sum_transformed_weights, + static_cast(transformed_capacity))); + } + + template + forceinline int + Pack::calc_dff_lower_bound(const int* weights, int n_weights, + int capacity, int n_not_zero_weights, + int max_weight, bool sanitize) { + LambdaRange lambda_range = l(capacity); + lambda_range = + sanitize ? sanitize_lambda_range(lambda_range, n_not_zero_weights, + max_weight) : + lambda_range; + if (lambda_range.min >= lambda_range.max) + return 0; + + int lower_bound = 0; + int l_step = + ceil_div_pp(lambda_range.max - lambda_range.min + 1, + n_lambda_samples + 1); + for (int lambda = lambda_range.min + l_step; + lambda < lambda_range.max; ) { + int cur_lower_bound = + calc_dff_lower_bound_single_lambda(weights, n_weights, + capacity, lambda); + lower_bound = std::max(lower_bound, cur_lower_bound); + if (lambda > lambda_range.max - l_step) + break; + lambda += l_step; + } + return lower_bound; + } + }}} // STATISTICS: int-prop - diff --git a/test/int/bin-packing.cpp b/test/int/bin-packing.cpp index dcf78ada03..40ad311993 100755 --- a/test/int/bin-packing.cpp +++ b/test/int/bin-packing.cpp @@ -309,6 +309,139 @@ namespace Test { namespace Int { } }; + /// Test that DFF lower bounds detect infeasible packing + class DFFLowerBound : public Base { + protected: + /// Simple test space class + class TestSpace : public Gecode::Space { + public: + /// Constructor + TestSpace(void) {} + /// Copy function + virtual Gecode::Space* copy(void) { + return nullptr; + } + }; + public: + /// Constructor + DFFLowerBound(void) + : Base("Int::BinPacking::DFFLowerBound") {} + /// Run the actual test + virtual bool run(void) { + using namespace Gecode; + TestSpace* home = new TestSpace; + IntVarArgs l(*home, 3, 0, 5); + IntVarArgs b(*home, 6, 0, 2); + IntArgs s({4,2,2,2,2,2}); + binpacking(*home, l, b, s, IPL_FULL); + bool failed = home->status() == SS_FAILED; + delete home; + return failed; + } + }; + + /// Test the bin-packing propagation levels + class PropagationLevels : public Base { + protected: + /// Simple test space class + class TestSpace : public Gecode::Space { + public: + TestSpace(void) {} + virtual Gecode::Space* copy(void) { + return nullptr; + } + }; + /// Check one propagation-level test case + bool check_case(int n_bins, int capacity, const Gecode::IntArgs& sizes, + const bool expected_failed[4]) const { + using namespace Gecode; + IntPropLevel const levels[] = + {IPL_BASIC, IPL_DEF, IPL_ADVANCED, IPL_FULL}; + for (unsigned int i = 0; i < 4; i += 1) { + TestSpace* home = new TestSpace; + IntVarArgs l(*home, n_bins, 0, capacity); + IntVarArgs b(*home, sizes.size(), 0, n_bins-1); + binpacking(*home, l, b, sizes, levels[i]); + bool failed = home->status() == SS_FAILED; + delete home; + if (failed != expected_failed[i]) + return false; + } + return true; + } + public: + /// Constructor + PropagationLevels(void) + : Base("Int::BinPacking::PropagationLevels") {} + /// Run the actual test + virtual bool run(void) { + using namespace Gecode; + bool const advanced_failed[] = {false, true, true, true}; + bool const full_failed[] = {false, false, false, true}; + return + check_case(3, 5, IntArgs({4,2,2,2,2,2}), advanced_failed) && + check_case(5, 35, + IntArgs({32,30,24,21,19,11,10,9,8,5,3}), + full_failed); + } + }; + + /// Test DFF arithmetic at the largest supported integer value + class DFFLargeWeights : public Base { + protected: + /// Simple test space class + class TestSpace : public Gecode::Space { + public: + /// Constructor + TestSpace(void) {} + /// Copy function + virtual Gecode::Space* copy(void) { + return nullptr; + } + }; + public: + /// Constructor + DFFLargeWeights(void) + : Base("Int::BinPacking::DFFLargeWeights") {} + /// Run the actual test + virtual bool run(void) { + using namespace Gecode; + { + TestSpace* home = new TestSpace; + IntVarArgs l(*home, 2, 0, Gecode::Int::Limits::max); + IntVarArgs b(*home, 1, 0, 1); + IntArgs s({Gecode::Int::Limits::max}); + binpacking(*home, l, b, s); + bool failed = home->status() == SS_FAILED; + delete home; + if (failed) + return false; + } + { + TestSpace* home = new TestSpace; + int c = Gecode::Int::Limits::max / 2; + IntVarArgs l(*home, 2, 0, c); + IntVarArgs b(*home, 1, 0, 1); + IntArgs s({c}); + binpacking(*home, l, b, s); + bool failed = home->status() == SS_FAILED; + delete home; + if (failed) + return false; + } + { + TestSpace* home = new TestSpace; + IntVarArgs l(*home, 2, 0, 1100000000); + IntVarArgs b(*home, 2, 0, 1); + IntArgs s({1000000000,1000000000}); + binpacking(*home, l, b, s); + bool failed = home->status() == SS_FAILED; + delete home; + return !failed; + } + } + }; + /// Help class to create and register tests class Create { public: @@ -392,6 +525,10 @@ namespace Test { namespace Int { (void) new CliqueMBPT(c48); (void) new CliqueMBPT(c49); } + + (void) new DFFLowerBound; + (void) new PropagationLevels; + (void) new DFFLargeWeights; } }; @@ -405,4 +542,3 @@ namespace Test { namespace Int { // STATISTICS: test-int -