Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhanced Bin Packing propagator #202

Open
wants to merge 2 commits into
base: release/6.3.0
Choose a base branch
from
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
31 changes: 31 additions & 0 deletions gecode/int/bin-packing.hh
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,18 @@ namespace Gecode { namespace Int { namespace BinPacking {
int operator [](int i) const;
};

/// Range of lambda values
struct LambdaRange {int min; int max;};

/// Integer Dynamic Array
using IntDynamicArray = Support::DynamicArray<int,Region>;

/**
* \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 +181,31 @@ namespace Gecode { namespace Int { namespace BinPacking {
virtual Actor* copy(Space& home);
/// Destructor
virtual size_t dispose(Space& home);
/// Reductions
static int const nReductions = 3;
static void calcReductions(const ViewArray<Item>& bs, const ViewArray<OffsetView>& l, IntDynamicArray & weightsBaseReduction, int & capacityBaseReduction, IntDynamicArray & deltaReductions);
/// Dual Feasible Functions
static int fCCM1(int w, int l, int c);
static int fMT(int w, int l, int c);
static int fBJ1(int w, int l, int c);
static int fVB2Base(int w, int l, int c);
static int fVB2(int w, int l, int c);
static int fFS1(int w, int l, int c);
static int fRAD2Base(int w, int l, int c);
static int fRAD2(int w, int l, int c);
static int const nLambdaSamples = 256;
static LambdaRange lCCM1(int c);
static LambdaRange lMT(int c);
static LambdaRange lBJ1(int c);
static LambdaRange lVB2(int c);
static LambdaRange lFS1(int c);
static LambdaRange lRAD2(int c);
static LambdaRange sanitizeLambdaRange(LambdaRange lambda, int nWeights, int maxWeight);
/// Lower bound
template<int f(int,int,int)>
static int calcDffLowerboundSingleLambda(const IntDynamicArray & weights, int capacity, int lambda);
template<int f(int,int,int), LambdaRange l(int)>
static int calcDffLowerbound(const IntDynamicArray & weights, int capacity, int nNotZeroWeights, int maxWeight, bool sanitize = false);
};


Expand Down
233 changes: 170 additions & 63 deletions gecode/int/bin-packing/propagate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,77 +278,136 @@ 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());
bool useDffLowerbounds = true;
if (not useDffLowerbounds)
{

// 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);
// 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;
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()]++;
// 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;
// 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++;
}
// 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);
// 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);
}
// 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;
// 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;
}
}
else
{
// Allocate auxiliary data
int nBins = l.size();
int nWeightsReductions = nBins + bs.size();
IntDynamicArray weightsBaseReduction(region, nWeightsReductions);
IntDynamicArray weightsCurrentReduction(region, nWeightsReductions);
IntDynamicArray deltaReductions(region, nReductions);

// Initialize reductions
int capacityBaseReduction = 0;
calcReductions(bs, l, weightsBaseReduction, capacityBaseReduction, deltaReductions);

for (int rIdx = 0; rIdx < nReductions; rIdx += 1)
{
// Calculate reduction parameters
int & delta = deltaReductions[rIdx];
int capacity = capacityBaseReduction + delta;

if (capacity > 0)
{
// Calculate reduction
int nNotZeroWeights = 0;
int maxWeight = 0;
IntDynamicArray & weights = weightsCurrentReduction;
for (int wIdx = 0; wIdx < nWeightsReductions; wIdx += 1)
{
int weight = weightsBaseReduction[wIdx] + (wIdx < nBins ? delta : 0);
nNotZeroWeights += weight != 0;
maxWeight = std::max(maxWeight, weight);
weights[wIdx] = weight;
}

// Check lowerbounds
int lowerbound = calcDffLowerbound<fCCM1, lCCM1>(weights, capacity, nNotZeroWeights, maxWeight);
if (lowerbound > nBins) return ES_FAILED;

lowerbound = calcDffLowerbound<fMT, lMT>(weights, capacity, nNotZeroWeights, maxWeight);
if (lowerbound > nBins) return ES_FAILED;

lowerbound = calcDffLowerbound<fBJ1, lBJ1>(weights, capacity, nNotZeroWeights, maxWeight);
if (lowerbound > nBins) return ES_FAILED;

lowerbound = calcDffLowerbound<fVB2, lVB2>(weights, capacity, nNotZeroWeights, maxWeight, true);
if (lowerbound > nBins) return ES_FAILED;

lowerbound = calcDffLowerbound<fFS1, lFS1>(weights, capacity, nNotZeroWeights, maxWeight, true);
if (lowerbound > nBins) return ES_FAILED;

lowerbound = calcDffLowerbound<fRAD2, lRAD2>(weights, capacity, nNotZeroWeights, maxWeight);
if (lowerbound > nBins) return ES_FAILED;
}
}
}
region.free();
}
Expand Down Expand Up @@ -394,6 +453,54 @@ namespace Gecode { namespace Int { namespace BinPacking {
}
}

void
Pack::calcReductions(const ViewArray<Item>& bs, const ViewArray<OffsetView>& l, IntDynamicArray & weightsBaseReduction, int & capacityBaseReduction, IntDynamicArray & deltaReductions)
{
// Reset values
int nWeightsReductions = weightsBaseReduction.capacity();
for (int wIdx = 0; wIdx < nWeightsReductions; wIdx += 1)
{
weightsBaseReduction[wIdx] = 0;
}

// R0
int nBins = l.size();
int nItems = bs.size();
capacityBaseReduction = 0;
for (int bIdx = 0; bIdx < nBins; bIdx += 1)
{
capacityBaseReduction = std::max(capacityBaseReduction, l[bIdx].max());
}
for (int bIdx = 0; bIdx < nBins; bIdx += 1)
{
weightsBaseReduction[bIdx] = capacityBaseReduction - l[bIdx].max();
}
for (int iIdx = 0; iIdx < nItems; iIdx += 1)
{
bool iAssigned = bs[iIdx].bin().assigned();
int iWeight = bs[iIdx].size();
if (iAssigned)
{
int bIdx = bs[iIdx].bin().val();
weightsBaseReduction[bIdx] += iWeight;
}
else
{
weightsBaseReduction[nBins + iIdx] = iWeight;
}
}

// RMin, RMax
int smallestVirtualWeight = std::numeric_limits<int>::max();
for (auto bIdx = 0; bIdx < nBins; bIdx += 1)
{
smallestVirtualWeight = std::min(smallestVirtualWeight, weightsBaseReduction[bIdx]);
}
deltaReductions[0] = -smallestVirtualWeight;
deltaReductions[1] = 0;
deltaReductions[2] = capacityBaseReduction - 2 * smallestVirtualWeight + 1;
}

}}}

// STATISTICS: int-prop
Expand Down
Loading