New dimension in bin packing to compute loads per bin and use it in steel

This commit is contained in:
lperron@google.com
2010-10-13 15:05:40 +00:00
parent bb200ad6a6
commit c91d538279
3 changed files with 172 additions and 10 deletions

View File

@@ -2943,6 +2943,11 @@ class Pack : public Constraint {
void AddWeightedSumLessOrEqualConstantDimension(const vector<int64>& weights,
const vector<int64>& bounds);
// This dimension imposes that for all bins b, the weighted sum
// (weights[i]) of all objects i assigned to 'b' is equal to loads[b].
void AddWeightedSumEqualVarDimension(const vector<int64>& weights,
const vector<IntVar*>& loads);
// This dimension enforces that cost_var == sum of weights[i] for
// all objects 'i' assigned to a bin.
void AddWeightedSumOfAssignedDimension(const vector<int64>& weights,

View File

@@ -527,6 +527,144 @@ class DimensionLessThanConstant : public Dimension {
int ranked_size_;
};
class DimensionWeightedSumEqVar : public Dimension {
public:
class VarDemon : public Demon {
public:
explicit VarDemon(DimensionWeightedSumEqVar* const dim,
int index) : dim_(dim), index_(index) {}
virtual ~VarDemon() {}
virtual void Run(Solver* const s) {
dim_->PushFromTop(index_);
}
private:
DimensionWeightedSumEqVar* const dim_;
const int index_;
};
DimensionWeightedSumEqVar(Solver* const s,
Pack* const p,
const int64* const weights,
int vars_count,
IntVar* const * loads,
int bins_count)
: Dimension(s, p),
vars_count_(vars_count),
weights_(new int64[vars_count_]),
bins_count_(bins_count),
loads_(new IntVar*[bins_count_]),
first_unbound_backward_vector_(bins_count, Rev<int>(0)),
sum_of_bound_variables_vector_(bins_count, Rev<int64>(0LL)),
sum_of_all_variables_vector_(bins_count, Rev<int64>(0LL)),
ranked_(new int64[vars_count_]),
ranked_size_(vars_count_) {
DCHECK(weights);
DCHECK(loads);
DCHECK_GT(vars_count, 0);
DCHECK_GT(bins_count, 0);
memcpy(weights_, weights, vars_count * sizeof(*weights));
memcpy(loads_.get(), loads, bins_count * sizeof(*loads));
for (int i = 0; i < vars_count_; ++i) {
ranked_[i] = i;
}
ranked_size_ = SortIndexByWeight(ranked_, weights_, vars_count_);
}
virtual ~DimensionWeightedSumEqVar() {
delete [] weights_;
delete [] ranked_;
}
virtual string DebugString() const {
return "DimensionWeightedSumEqVar";
}
virtual void Post() {
for (int i = 0; i < bins_count_; ++i) {
Demon* const d = solver()->RevAlloc(new VarDemon(this, i));
loads_[i]->WhenRange(d);
}
}
void PushFromTop(int64 bin_index) {
IntVar* const load = loads_[bin_index];
load->SetRange(sum_of_bound_variables_vector_[bin_index].Value(),
sum_of_all_variables_vector_[bin_index].Value());
const int64 slack_up =
load->Max() - sum_of_bound_variables_vector_[bin_index].Value();
const int64 slack_down =
sum_of_all_variables_vector_[bin_index].Value() - load->Min();
int64 last_unbound = first_unbound_backward_vector_[bin_index].Value();
for (; last_unbound >= 0; --last_unbound) {
const int64 var_index = ranked_[last_unbound];
const int64 weight = weights_[var_index];
if (IsUndecided(var_index, bin_index)) {
if (weight > slack_up) {
SetImpossible(var_index, bin_index);
} else if (weight > slack_down) {
Assign(var_index, bin_index);
} else {
break;
}
}
}
first_unbound_backward_vector_[bin_index].SetValue(solver(), last_unbound);
}
virtual void InitialPropagate(int64 bin_index,
const vector<int64>& forced,
const vector<int64>& undecided) {
Solver* const s = solver();
int64 sum = 0LL;
for (ConstIter<vector<int64> > it(forced); !it.at_end(); ++it) {
sum += weights_[*it];
}
sum_of_bound_variables_vector_[bin_index].SetValue(s, sum);
for (ConstIter<vector<int64> > it(undecided); !it.at_end(); ++it) {
sum += weights_[*it];
}
sum_of_all_variables_vector_[bin_index].SetValue(s, sum);
first_unbound_backward_vector_[bin_index].SetValue(s, ranked_size_ - 1);
PushFromTop(bin_index);
}
virtual void EndInitialPropagate() {}
virtual void Propagate(int64 bin_index,
const vector<int64>& forced,
const vector<int64>& removed) {
Solver* const s = solver();
int64 down = sum_of_bound_variables_vector_[bin_index].Value();
for (ConstIter<vector<int64> > it(forced); !it.at_end(); ++it) {
down += weights_[*it];
}
sum_of_bound_variables_vector_[bin_index].SetValue(s, down);
int64 up = sum_of_all_variables_vector_[bin_index].Value();
for (ConstIter<vector<int64> > it(removed); !it.at_end(); ++it) {
up -= weights_[*it];
}
sum_of_all_variables_vector_[bin_index].SetValue(s, up);
PushFromTop(bin_index);
}
virtual void InitialPropagateUnassigned(const vector<int64>& assigned,
const vector<int64>& unassigned) {}
virtual void PropagateUnassigned(const vector<int64>& assigned,
const vector<int64>& unassigned) {}
virtual void EndPropagate() {}
private:
const int vars_count_;
int64* weights_;
const int bins_count_;
scoped_array<IntVar*> loads_;
vector<Rev<int> > first_unbound_backward_vector_;
vector<Rev<int64> > sum_of_bound_variables_vector_;
vector<Rev<int64> > sum_of_all_variables_vector_;
int64* ranked_;
int ranked_size_;
};
class AssignedWeightedSumDimension : public Dimension {
public:
class VarDemon : public Demon {
@@ -887,6 +1025,21 @@ void Pack::AddWeightedSumLessOrEqualConstantDimension(
dims_.push_back(dim);
}
void Pack::AddWeightedSumEqualVarDimension(const vector<int64>& weights,
const vector<IntVar*>& loads) {
DCHECK_EQ(weights.size(), vsize_);
DCHECK_EQ(loads.size(), bins_);
Solver* const s = solver();
Dimension* const dim =
s->RevAlloc(new DimensionWeightedSumEqVar(s,
this,
weights.data(),
weights.size(),
loads.data(),
loads.size()));
dims_.push_back(dim);
}
void Pack::AddWeightedSumOfAssignedDimension(const vector<int64>& weights,
IntVar* const cost_var) {
DCHECK_EQ(weights.size(), vsize_);

View File

@@ -27,15 +27,15 @@ gflags.DEFINE_string('data', 'python/data/steel_mill/steel_mill_slab.txt',
#----------------helper for binpacking posting----------------
def BinPacking(cp, binvars, weights, loadvars):
def BinPacking(solver, binvars, weights, loadvars):
'''post the load constraint on bins.
constraints forall j: loadvars[j] == sum_i (binvars[i] == j) * weights[i])
'''
for j in range(len(loadvars)):
b = [cp.IsEqualCstVar(binvars[i], j) for i in range(len(binvars))]
cp.Add(cp.ScalProd(b, weights) == loadvars[j])
cp.Add(cp.SumEquality(loadvars, sum(weights)))
pack = solver.Pack(binvars, len(binvars))
pack.AddWeightedSumEqualVarDimension(weights, loadvars);
solver.Add(pack)
solver.Add(solver.SumEquality(loadvars, sum(weights)))
#------------------------------data reading-------------------
@@ -65,11 +65,12 @@ class SteelDecisionBuilder(pywrapcp.PyDecisionBuilder):
'''Dedicated Decision Builder for steel mill slab.
Search for the steel mill slab problem with Dynamic Symmetry
Breaking during search is an adaptation (for binary tree) from the paper of Pascal Van Hentenryck and
Laurent Michel CPAIOR-2008.
Breaking during search is an adaptation (for binary tree) from the
paper of Pascal Van Hentenryck and Laurent Michel CPAIOR-2008.
The value heuristic comes from the paper
Solving Steel Mill Slab Problems with Constraint-Based Techniques: CP, LNS, and CBLS,
Solving Steel Mill Slab Problems with Constraint-Based Techniques:
CP, LNS, and CBLS,
Schaus et. al. to appear in Constraints 2010
'''
@@ -87,11 +88,14 @@ class SteelDecisionBuilder(pywrapcp.PyDecisionBuilder):
if var:
v = self.MaxBound()
if v + 1 == var.Min():
# Symmetry breaking. If you need to assign to a new bin,
# select the first one.
solver.Add(var == v + 1)
return self.Next(solver)
else:
#value heuristic (important for difficult problem):
# try first to place the order in the slab that will induce the least increase of the loss
# value heuristic (important for difficult problem):
# try first to place the order in the slab that will induce
# the least increase of the loss
loads = self.getLoads()
l,v = min((self.__losstab[loads[i]+weight],i)
for i in range(var.Min(),var.Max()+1)