diff --git a/src/constraint_solver/expr_array.cc b/src/constraint_solver/expr_array.cc index 09ddf99761..e4440be197 100644 --- a/src/constraint_solver/expr_array.cc +++ b/src/constraint_solver/expr_array.cc @@ -658,6 +658,127 @@ class MinConstraint : public TreeArrayConstraint { Demon* min_demon_; }; +class SmallMinConstraint : public Constraint { + public: + SmallMinConstraint(Solver* const solver, const std::vector& 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 vars_; + IntVar* const target_var_; + Rev target_var_min_; + Rev target_var_max_; +}; + // ---------- Max Array ---------- // This constraint implements std::max(vars) == max_var. @@ -2899,7 +3020,11 @@ IntExpr* Solver::MakeMin(const std::vector& 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& 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)); }