create small min constraint

This commit is contained in:
lperron@google.com
2014-07-18 00:14:31 +00:00
parent b4c4005550
commit 5734fdf486

View File

@@ -658,6 +658,127 @@ class MinConstraint : public TreeArrayConstraint {
Demon* min_demon_;
};
class SmallMinConstraint : public Constraint {
public:
SmallMinConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
IntVar* const target_var)
: Constraint(solver), vars_(vars), target_var_(target_var),
target_var_min_(0), target_var_max_(0) {}
virtual ~SmallMinConstraint() {}
virtual void Post() {
for (int i = 0; i < vars_.size(); ++i) {
Demon* const demon = MakeConstraintDemon1(
solver(), this, &SmallMinConstraint::VarChanged, "VarChanged", i);
vars_[i]->WhenRange(demon);
}
Demon* const mdemon = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
solver(), this, &SmallMinConstraint::MinVarChanged, "MinVarChanged"));
target_var_->WhenRange(mdemon);
}
virtual void InitialPropagate() {
int64 min_min = kint64max;
int64 min_max = kint64max;
for (IntVar* const var : vars_) {
min_min = std::min(min_min, var->Min());
min_max = std::min(min_max, var->Max());
}
target_var_min_.SetValue(solver(), min_min);
target_var_max_.SetValue(solver(), min_max);
// Propagate to min_var.
target_var_->SetRange(min_min, min_max);
// Push down.
MinVarChanged();
}
std::string DebugString() const {
return StringPrintf("SmallMin(%s) == %s",
JoinDebugStringPtr(vars_, ", ").c_str(),
target_var_->DebugString().c_str());
}
void Accept(ModelVisitor* const visitor) const {
visitor->BeginVisitConstraint(ModelVisitor::kMinEqual, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
vars_);
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
target_var_);
visitor->EndVisitConstraint(ModelVisitor::kMinEqual, this);
}
private:
void VarChanged(int index) {
IntVar* const var = vars_[index];
const int64 old_min = var->OldMin();
const int64 var_min = var->Min();
const int64 var_max = var->Max();
if ((old_min == target_var_min_.Value() && old_min != var_min) ||
var_max < target_var_max_.Value()) {
// Can influence the min var bounds.
int64 min_min = kint64max;
int64 min_max = kint64max;
for (IntVar* const var : vars_) {
min_min = std::min(min_min, var->Min());
min_max = std::min(min_max, var->Max());
}
if (min_min > target_var_min_.Value() ||
min_max < target_var_max_.Value()) {
target_var_min_.SetValue(solver(), min_min);
target_var_max_.SetValue(solver(), min_max);
target_var_->SetRange(target_var_min_.Value(), target_var_max_.Value());
}
}
MinVarChanged();
}
void MinVarChanged() {
const int64 new_min = target_var_->Min();
const int64 new_max = target_var_->Max();
// Nothing to do?
if (new_min <= target_var_min_.Value() &&
new_max >= target_var_max_.Value()) {
return;
}
IntVar* candidate = nullptr;
int active = 0;
if (new_max < target_var_max_.Value()) {
// Look if only one candidate to push the max down.
for (IntVar* const var : vars_) {
if (var->Min() <= new_max) {
if (active++ >= 1) {
break;
}
candidate = var;
}
}
if (active == 0) {
solver()->Fail();
}
}
if (target_var_min_.Value() < new_min) {
if (active == 1) {
candidate->SetRange(new_min, new_max);
} else {
for (IntVar* const var : vars_) {
var->SetMin(new_min);
}
}
} else if (active == 1) {
candidate->SetMax(new_max);
}
}
std::vector<IntVar*> vars_;
IntVar* const target_var_;
Rev<int64> target_var_min_;
Rev<int64> target_var_max_;
};
// ---------- Max Array ----------
// This constraint implements std::max(vars) == max_var.
@@ -2899,7 +3020,11 @@ IntExpr* Solver::MakeMin(const std::vector<IntVar*>& vars) {
new_max = std::min(new_max, vars[i]->Max());
}
IntVar* const new_var = MakeIntVar(new_min, new_max);
AddConstraint(RevAlloc(new MinConstraint(this, vars, new_var)));
if (size < parameters_.array_split_size) {
AddConstraint(RevAlloc(new SmallMinConstraint(this, vars, new_var)));
} else {
AddConstraint(RevAlloc(new MinConstraint(this, vars, new_var)));
}
model_cache_->InsertVarArrayExpression(new_var, vars,
ModelCache::VAR_ARRAY_MIN);
return new_var;
@@ -2951,6 +3076,8 @@ Constraint* Solver::MakeMinEquality(const std::vector<IntVar*>& vars,
if (size > 2) {
if (AreAllBooleans(vars)) {
return RevAlloc(new ArrayBoolAndEq(this, vars, min_var));
} else if (size < parameters_.array_split_size) {
return RevAlloc(new SmallMinConstraint(this, vars, min_var));
} else {
return RevAlloc(new MinConstraint(this, vars, min_var));
}