Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions changelog.in
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ This release modernizes the Gecode build infrastructure, adds a
first-class CMake package for downstream consumers, refreshes the
autoconf build path, and updates CI coverage for current platforms.

[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
Expand Down
42 changes: 42 additions & 0 deletions gecode/int/bin-packing.hh
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
* Christian Schulte <schulte@gecode.dev>
*
* Contributing authors:
* Fabio Tardivo <ftardivo@nmsu.edu>
* Stefano Gualandi <stefano.gualandi@gmail.com>
*
* Copyright:
* Fabio Tardivo, 2024
* Stefano Gualandi, 2013
* Christian Schulte, 2010
*
Expand Down Expand Up @@ -127,12 +129,18 @@ 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:
* Paul Shaw. A Constraint for Bin Packing. CP 2004.
* Tardivo et al. CP for Bin Packing with Multi-Core and GPUs. CP 2024.
*
* Requires \code #include <gecode/int/bin-packing.hh> \endcode
*
Expand Down Expand Up @@ -175,6 +183,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<Item>& bs,
const ViewArray<OffsetView>& 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<int f(int,int,int)>
static int calc_dff_lower_bound_single_lambda(const int* weights,
int n_weights,
int capacity, int lambda);
template<int f(int,int,int), LambdaRange l(int)>
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);
};


Expand Down
194 changes: 123 additions & 71 deletions gecode/int/bin-packing/propagate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
* Main authors:
* Christian Schulte <schulte@gecode.dev>
*
* Contributing authors:
* Fabio Tardivo <ftardivo@nmsu.edu>
*
* Copyright:
* Fabio Tardivo, 2024
* Christian Schulte, 2010
*
* This file is part of Gecode, the generic constraint
Expand Down Expand Up @@ -278,77 +282,83 @@ 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<m; j++)
c = std::max(c,l[j].max());

// Count how many items have a certain size (bucket sort)
int* n_s = region.alloc<int>(c+1);

for (int i=0; i<c+1; i++)
n_s[i] = 0;

// Count unpacked items
for (int i=0; i<n; i++)
n_s[bs[i].size()]++;

// Number of items and remaining bin load
int nm = n;

// Only count positive remaining bin loads
for (int j=0; j<m; j++)
if (l[j].max() < 0) {
return ES_FAILED;
} else if (c > l[j].max()) {
n_s[c - l[j].max()]++; nm++;
}

// Sizes of items and remaining bin loads
int* s = region.alloc<int>(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<int>(n_weights_reductions);
int* weights_current_reduction =
region.alloc<int>(n_weights_reductions);
int* delta_reductions = region.alloc<int>(n_reductions);

// 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];
int capacity = capacity_base_reduction + delta;

if (capacity > 0) {
// 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 lower bounds
int lower_bound =
calc_dff_lower_bound<f_ccm1, l_ccm1>
(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<f_mt, l_mt>
(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<f_bj1, l_bj1>
(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<f_vb2, l_vb2>
(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<f_fs1, l_fs1>
(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<f_rad2, l_rad2>
(weights, n_weights_reductions, capacity,
n_not_zero_weights, max_weight);
if (lower_bound > n_bins)
return ES_FAILED;
}
}
}
region.free();
}
Expand Down Expand Up @@ -394,7 +404,49 @@ namespace Gecode { namespace Int { namespace BinPacking {
}
}

void
Pack::calc_reductions(const ViewArray<Item>& bs,
const ViewArray<OffsetView>& 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<int>::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 - 2 * smallest_virtual_weight + 1;
}

}}}

// STATISTICS: int-prop

Loading