create small min constraint
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user