2024-01-04 13:43:15 +01:00
|
|
|
// Copyright 2010-2024 Google LLC
|
2010-09-15 12:42:33 +00:00
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
2014-07-09 15:18:27 +00:00
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
//
|
|
|
|
|
// Expression constraints
|
|
|
|
|
|
2022-05-18 16:36:05 +02:00
|
|
|
#include <algorithm>
|
2013-12-16 10:24:42 +00:00
|
|
|
#include <cstddef>
|
2021-04-01 12:13:35 +02:00
|
|
|
#include <cstdint>
|
|
|
|
|
#include <limits>
|
2013-07-29 02:49:12 +00:00
|
|
|
#include <set>
|
2011-09-21 15:16:48 +00:00
|
|
|
#include <string>
|
2022-05-18 16:36:05 +02:00
|
|
|
#include <utility>
|
2011-09-21 15:16:48 +00:00
|
|
|
#include <vector>
|
|
|
|
|
|
2018-10-31 16:18:18 +01:00
|
|
|
#include "absl/strings/str_format.h"
|
|
|
|
|
#include "absl/strings/str_join.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
#include "ortools/base/commandlineflags.h"
|
2018-06-08 16:40:43 +02:00
|
|
|
#include "ortools/base/logging.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
#include "ortools/base/stl_util.h"
|
2023-08-18 16:14:58 +02:00
|
|
|
#include "ortools/base/types.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
#include "ortools/constraint_solver/constraint_solver.h"
|
|
|
|
|
#include "ortools/constraint_solver/constraint_solveri.h"
|
|
|
|
|
#include "ortools/util/saturated_arithmetic.h"
|
|
|
|
|
#include "ortools/util/sorted_interval_list.h"
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2020-11-01 11:47:28 +01:00
|
|
|
ABSL_FLAG(int, cache_initial_size, 1024,
|
2020-10-21 15:51:44 +02:00
|
|
|
"Initial size of the array of the hash "
|
|
|
|
|
"table of caches for objects of type Var(x == 3)");
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
namespace operations_research {
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Equality
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2010-09-15 12:42:33 +00:00
|
|
|
class EqualityExprCst : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2023-05-31 14:27:08 +02:00
|
|
|
EqualityExprCst(Solver* s, IntExpr* e, int64_t v);
|
2015-04-16 15:49:51 +02:00
|
|
|
~EqualityExprCst() override {}
|
|
|
|
|
void Post() override;
|
|
|
|
|
void InitialPropagate() override;
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* Var() override {
|
2012-03-19 17:14:57 +00:00
|
|
|
return solver()->MakeIsEqualCstVar(expr_->Var(), value_);
|
|
|
|
|
}
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override;
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kEquality, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
2011-07-11 20:13:14 +00:00
|
|
|
expr_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, value_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kEquality, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const expr_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t value_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
EqualityExprCst::EqualityExprCst(Solver* const s, IntExpr* const e, int64_t v)
|
2013-10-10 15:23:20 +00:00
|
|
|
: Constraint(s), expr_(e), value_(v) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
void EqualityExprCst::Post() {
|
|
|
|
|
if (!expr_->IsVar()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* d = solver()->MakeConstraintInitialPropagateCallback(this);
|
2010-09-15 12:42:33 +00:00
|
|
|
expr_->WhenRange(d);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-10 15:23:20 +00:00
|
|
|
void EqualityExprCst::InitialPropagate() { expr_->SetValue(value_); }
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2013-12-16 10:24:42 +00:00
|
|
|
std::string EqualityExprCst::DebugString() const {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("(%s == %d)", expr_->DebugString(), value_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeEquality(IntExpr* const e, int64_t v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* left = nullptr;
|
|
|
|
|
IntExpr* right = nullptr;
|
2012-07-06 14:05:06 +00:00
|
|
|
if (IsADifference(e, &left, &right)) {
|
|
|
|
|
return MakeEquality(left, MakeSum(right, v));
|
2012-07-05 14:22:07 +00:00
|
|
|
} else if (e->IsVar() && !e->Var()->Contains(v)) {
|
|
|
|
|
return MakeFalseConstraint();
|
|
|
|
|
} else if (e->Min() == e->Max() && e->Min() == v) {
|
|
|
|
|
return MakeTrueConstraint();
|
2012-06-29 09:41:24 +00:00
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new EqualityExprCst(this, e, v));
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeEquality(IntExpr* const e, int v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* left = nullptr;
|
|
|
|
|
IntExpr* right = nullptr;
|
2012-07-06 14:05:06 +00:00
|
|
|
if (IsADifference(e, &left, &right)) {
|
|
|
|
|
return MakeEquality(left, MakeSum(right, v));
|
2012-07-05 14:22:07 +00:00
|
|
|
} else if (e->IsVar() && !e->Var()->Contains(v)) {
|
|
|
|
|
return MakeFalseConstraint();
|
|
|
|
|
} else if (e->Min() == e->Max() && e->Min() == v) {
|
|
|
|
|
return MakeTrueConstraint();
|
2012-06-29 09:41:24 +00:00
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new EqualityExprCst(this, e, v));
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Greater or equal constraint
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2010-09-15 12:42:33 +00:00
|
|
|
class GreaterEqExprCst : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2023-05-31 14:27:08 +02:00
|
|
|
GreaterEqExprCst(Solver* s, IntExpr* e, int64_t v);
|
2015-04-16 15:49:51 +02:00
|
|
|
~GreaterEqExprCst() override {}
|
|
|
|
|
void Post() override;
|
|
|
|
|
void InitialPropagate() override;
|
|
|
|
|
std::string DebugString() const override;
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* Var() override {
|
2012-03-19 17:14:57 +00:00
|
|
|
return solver()->MakeIsGreaterOrEqualCstVar(expr_->Var(), value_);
|
|
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kGreaterOrEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
2011-07-11 20:13:14 +00:00
|
|
|
expr_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, value_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kGreaterOrEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const expr_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t value_;
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* demon_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
GreaterEqExprCst::GreaterEqExprCst(Solver* const s, IntExpr* const e, int64_t v)
|
2014-01-16 23:42:44 +00:00
|
|
|
: Constraint(s), expr_(e), value_(v), demon_(nullptr) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
void GreaterEqExprCst::Post() {
|
2014-01-16 17:27:42 +00:00
|
|
|
if (!expr_->IsVar() && expr_->Min() < value_) {
|
2014-01-16 23:42:44 +00:00
|
|
|
demon_ = solver()->MakeConstraintInitialPropagateCallback(this);
|
|
|
|
|
expr_->WhenRange(demon_);
|
|
|
|
|
} else {
|
2014-01-17 18:36:18 +00:00
|
|
|
// Let's clean the demon in case the constraint is posted during search.
|
2014-01-16 23:42:44 +00:00
|
|
|
demon_ = nullptr;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-16 23:42:44 +00:00
|
|
|
void GreaterEqExprCst::InitialPropagate() {
|
|
|
|
|
expr_->SetMin(value_);
|
|
|
|
|
if (demon_ != nullptr && expr_->Min() >= value_) {
|
|
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2013-12-16 10:24:42 +00:00
|
|
|
std::string GreaterEqExprCst::DebugString() const {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("(%s >= %d)", expr_->DebugString(), value_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeGreaterOrEqual(IntExpr* const e, int64_t v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2014-05-15 05:38:09 +00:00
|
|
|
if (e->Min() >= v) {
|
|
|
|
|
return MakeTrueConstraint();
|
|
|
|
|
} else if (e->Max() < v) {
|
|
|
|
|
return MakeFalseConstraint();
|
|
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new GreaterEqExprCst(this, e, v));
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeGreaterOrEqual(IntExpr* const e, int v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2014-05-15 05:38:09 +00:00
|
|
|
if (e->Min() >= v) {
|
|
|
|
|
return MakeTrueConstraint();
|
|
|
|
|
} else if (e->Max() < v) {
|
|
|
|
|
return MakeFalseConstraint();
|
|
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new GreaterEqExprCst(this, e, v));
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeGreater(IntExpr* const e, int64_t v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2014-05-15 05:38:09 +00:00
|
|
|
if (e->Min() > v) {
|
|
|
|
|
return MakeTrueConstraint();
|
|
|
|
|
} else if (e->Max() <= v) {
|
|
|
|
|
return MakeFalseConstraint();
|
|
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new GreaterEqExprCst(this, e, v + 1));
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeGreater(IntExpr* const e, int v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2014-05-15 05:38:09 +00:00
|
|
|
if (e->Min() > v) {
|
|
|
|
|
return MakeTrueConstraint();
|
|
|
|
|
} else if (e->Max() <= v) {
|
|
|
|
|
return MakeFalseConstraint();
|
|
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new GreaterEqExprCst(this, e, v + 1));
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Less or equal constraint
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2010-09-15 12:42:33 +00:00
|
|
|
class LessEqExprCst : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2023-05-31 14:27:08 +02:00
|
|
|
LessEqExprCst(Solver* s, IntExpr* e, int64_t v);
|
2015-04-16 15:49:51 +02:00
|
|
|
~LessEqExprCst() override {}
|
|
|
|
|
void Post() override;
|
|
|
|
|
void InitialPropagate() override;
|
|
|
|
|
std::string DebugString() const override;
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* Var() override {
|
2012-03-19 17:14:57 +00:00
|
|
|
return solver()->MakeIsLessOrEqualCstVar(expr_->Var(), value_);
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kLessOrEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
2011-07-11 20:13:14 +00:00
|
|
|
expr_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, value_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kLessOrEqual, this);
|
|
|
|
|
}
|
2013-10-10 15:23:20 +00:00
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const expr_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t value_;
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* demon_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
LessEqExprCst::LessEqExprCst(Solver* const s, IntExpr* const e, int64_t v)
|
2014-01-16 23:42:44 +00:00
|
|
|
: Constraint(s), expr_(e), value_(v), demon_(nullptr) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
void LessEqExprCst::Post() {
|
2014-01-16 17:27:42 +00:00
|
|
|
if (!expr_->IsVar() && expr_->Max() > value_) {
|
2014-01-16 23:42:44 +00:00
|
|
|
demon_ = solver()->MakeConstraintInitialPropagateCallback(this);
|
|
|
|
|
expr_->WhenRange(demon_);
|
|
|
|
|
} else {
|
2014-01-17 18:36:18 +00:00
|
|
|
// Let's clean the demon in case the constraint is posted during search.
|
2014-01-16 23:42:44 +00:00
|
|
|
demon_ = nullptr;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-16 23:42:44 +00:00
|
|
|
void LessEqExprCst::InitialPropagate() {
|
|
|
|
|
expr_->SetMax(value_);
|
|
|
|
|
if (demon_ != nullptr && expr_->Max() <= value_) {
|
|
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2013-12-16 10:24:42 +00:00
|
|
|
std::string LessEqExprCst::DebugString() const {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("(%s <= %d)", expr_->DebugString(), value_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeLessOrEqual(IntExpr* const e, int64_t v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2014-05-15 05:38:09 +00:00
|
|
|
if (e->Max() <= v) {
|
|
|
|
|
return MakeTrueConstraint();
|
|
|
|
|
} else if (e->Min() > v) {
|
|
|
|
|
return MakeFalseConstraint();
|
|
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new LessEqExprCst(this, e, v));
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeLessOrEqual(IntExpr* const e, int v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2014-05-15 05:38:09 +00:00
|
|
|
if (e->Max() <= v) {
|
|
|
|
|
return MakeTrueConstraint();
|
|
|
|
|
} else if (e->Min() > v) {
|
|
|
|
|
return MakeFalseConstraint();
|
|
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new LessEqExprCst(this, e, v));
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeLess(IntExpr* const e, int64_t v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2014-05-15 05:38:09 +00:00
|
|
|
if (e->Max() < v) {
|
|
|
|
|
return MakeTrueConstraint();
|
|
|
|
|
} else if (e->Min() >= v) {
|
|
|
|
|
return MakeFalseConstraint();
|
|
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new LessEqExprCst(this, e, v - 1));
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeLess(IntExpr* const e, int v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2014-05-15 05:38:09 +00:00
|
|
|
if (e->Max() < v) {
|
|
|
|
|
return MakeTrueConstraint();
|
|
|
|
|
} else if (e->Min() >= v) {
|
|
|
|
|
return MakeFalseConstraint();
|
|
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new LessEqExprCst(this, e, v - 1));
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Different constraints
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2010-09-15 12:42:33 +00:00
|
|
|
class DiffCst : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2023-05-31 14:27:08 +02:00
|
|
|
DiffCst(Solver* s, IntVar* var, int64_t value);
|
2015-04-16 15:49:51 +02:00
|
|
|
~DiffCst() override {}
|
|
|
|
|
void Post() override {}
|
|
|
|
|
void InitialPropagate() override;
|
2010-09-15 12:42:33 +00:00
|
|
|
void BoundPropagate();
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override;
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* Var() override {
|
2012-03-19 17:14:57 +00:00
|
|
|
return solver()->MakeIsDifferentCstVar(var_, value_);
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kNonEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
2011-07-11 20:13:14 +00:00
|
|
|
var_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, value_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kNonEqual, this);
|
|
|
|
|
}
|
2013-10-10 15:23:20 +00:00
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
bool HasLargeDomain(IntVar* var);
|
2018-12-03 14:26:31 +01:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t value_;
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* demon_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
DiffCst::DiffCst(Solver* const s, IntVar* const var, int64_t value)
|
2013-10-10 15:23:20 +00:00
|
|
|
: Constraint(s), var_(var), value_(value), demon_(nullptr) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
void DiffCst::InitialPropagate() {
|
2018-12-03 14:26:31 +01:00
|
|
|
if (HasLargeDomain(var_)) {
|
2013-10-10 15:23:20 +00:00
|
|
|
demon_ = MakeConstraintDemon0(solver(), this, &DiffCst::BoundPropagate,
|
2010-09-15 12:42:33 +00:00
|
|
|
"BoundPropagate");
|
|
|
|
|
var_->WhenRange(demon_);
|
|
|
|
|
} else {
|
|
|
|
|
var_->RemoveValue(value_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffCst::BoundPropagate() {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t var_min = var_->Min();
|
|
|
|
|
const int64_t var_max = var_->Max();
|
2010-09-15 12:42:33 +00:00
|
|
|
if (var_min > value_ || var_max < value_) {
|
|
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
} else if (var_min == value_) {
|
2022-09-05 18:30:09 +02:00
|
|
|
var_->SetMin(CapAdd(value_, 1));
|
2010-09-15 12:42:33 +00:00
|
|
|
} else if (var_max == value_) {
|
2022-09-05 18:30:09 +02:00
|
|
|
var_->SetMax(CapSub(value_, 1));
|
2018-12-03 14:26:31 +01:00
|
|
|
} else if (!HasLargeDomain(var_)) {
|
2010-09-15 12:42:33 +00:00
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
var_->RemoveValue(value_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-16 10:24:42 +00:00
|
|
|
std::string DiffCst::DebugString() const {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("(%s != %d)", var_->DebugString(), value_);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
bool DiffCst::HasLargeDomain(IntVar* var) {
|
2018-12-03 14:26:31 +01:00
|
|
|
return CapSub(var->Max(), var->Min()) > 0xFFFFFF;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeNonEquality(IntExpr* const e, int64_t v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* left = nullptr;
|
|
|
|
|
IntExpr* right = nullptr;
|
2012-07-06 14:05:06 +00:00
|
|
|
if (IsADifference(e, &left, &right)) {
|
|
|
|
|
return MakeNonEquality(left, MakeSum(right, v));
|
2012-07-05 16:27:57 +00:00
|
|
|
} else if (e->IsVar() && !e->Var()->Contains(v)) {
|
2012-07-05 14:22:07 +00:00
|
|
|
return MakeTrueConstraint();
|
|
|
|
|
} else if (e->Bound() && e->Min() == v) {
|
|
|
|
|
return MakeFalseConstraint();
|
2012-06-29 09:41:24 +00:00
|
|
|
} else {
|
2012-07-05 16:27:57 +00:00
|
|
|
return RevAlloc(new DiffCst(this, e->Var(), v));
|
2012-06-29 09:41:24 +00:00
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeNonEquality(IntExpr* const e, int v) {
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, e->solver());
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* left = nullptr;
|
|
|
|
|
IntExpr* right = nullptr;
|
2012-07-06 14:05:06 +00:00
|
|
|
if (IsADifference(e, &left, &right)) {
|
|
|
|
|
return MakeNonEquality(left, MakeSum(right, v));
|
2012-07-05 16:27:57 +00:00
|
|
|
} else if (e->IsVar() && !e->Var()->Contains(v)) {
|
2012-07-05 14:22:07 +00:00
|
|
|
return MakeTrueConstraint();
|
|
|
|
|
} else if (e->Bound() && e->Min() == v) {
|
|
|
|
|
return MakeFalseConstraint();
|
2012-06-29 09:41:24 +00:00
|
|
|
} else {
|
2012-07-05 16:27:57 +00:00
|
|
|
return RevAlloc(new DiffCst(this, e->Var(), v));
|
2012-06-29 09:41:24 +00:00
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
// ----- is_equal_cst Constraint -----
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2011-12-17 21:20:58 +00:00
|
|
|
class IsEqualCstCt : public CastConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2021-04-01 12:13:35 +02:00
|
|
|
IsEqualCstCt(Solver* const s, IntVar* const v, int64_t c, IntVar* const b)
|
2013-10-10 15:23:20 +00:00
|
|
|
: CastConstraint(s, b), var_(v), cst_(c), demon_(nullptr) {}
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
demon_ = solver()->MakeConstraintInitialPropagateCallback(this);
|
|
|
|
|
var_->WhenDomain(demon_);
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_->WhenBound(demon_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
bool inhibit = var_->Bound();
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t u = var_->Contains(cst_);
|
|
|
|
|
int64_t l = inhibit ? u : 0;
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_->SetRange(l, u);
|
|
|
|
|
if (target_var_->Bound()) {
|
|
|
|
|
if (target_var_->Min() == 0) {
|
2017-02-07 17:44:35 +01:00
|
|
|
if (var_->Size() <= 0xFFFFFF) {
|
|
|
|
|
var_->RemoveValue(cst_);
|
|
|
|
|
inhibit = true;
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
} else {
|
|
|
|
|
var_->SetValue(cst_);
|
2017-02-07 17:44:35 +01:00
|
|
|
inhibit = true;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (inhibit) {
|
|
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("IsEqualCstCt(%s, %d, %s)", var_->DebugString(),
|
|
|
|
|
cst_, target_var_->DebugString());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kIsEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
2011-07-11 20:13:14 +00:00
|
|
|
var_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, cst_);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kIsEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst_;
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* demon_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
IntVar* Solver::MakeIsEqualCstVar(IntExpr* const var, int64_t value) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* left = nullptr;
|
|
|
|
|
IntExpr* right = nullptr;
|
2012-07-06 14:05:06 +00:00
|
|
|
if (IsADifference(var, &left, &right)) {
|
|
|
|
|
return MakeIsEqualVar(left, MakeSum(right, value));
|
2012-06-29 09:41:24 +00:00
|
|
|
}
|
2017-12-08 14:52:49 +01:00
|
|
|
if (CapSub(var->Max(), var->Min()) == 1) {
|
2012-06-29 14:34:23 +00:00
|
|
|
if (value == var->Min()) {
|
|
|
|
|
return MakeDifference(value + 1, var)->Var();
|
|
|
|
|
} else if (value == var->Max()) {
|
|
|
|
|
return MakeSum(var, -value + 1)->Var();
|
|
|
|
|
} else {
|
|
|
|
|
return MakeIntConst(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-05 16:27:57 +00:00
|
|
|
if (var->IsVar()) {
|
|
|
|
|
return var->Var()->IsEqual(value);
|
|
|
|
|
} else {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const boolvar =
|
2018-12-03 14:26:31 +01:00
|
|
|
MakeBoolVar(absl::StrFormat("Is(%s == %d)", var->DebugString(), value));
|
2012-07-05 16:27:57 +00:00
|
|
|
AddConstraint(MakeIsEqualCstCt(var, value, boolvar));
|
|
|
|
|
return boolvar;
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeIsEqualCstCt(IntExpr* const var, int64_t value,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const boolvar) {
|
2011-09-08 00:36:56 +00:00
|
|
|
CHECK_EQ(this, var->solver());
|
|
|
|
|
CHECK_EQ(this, boolvar->solver());
|
|
|
|
|
if (value == var->Min()) {
|
2017-12-08 14:52:49 +01:00
|
|
|
if (CapSub(var->Max(), var->Min()) == 1) {
|
2012-09-03 21:40:45 +00:00
|
|
|
return MakeEquality(MakeDifference(value + 1, var), boolvar);
|
|
|
|
|
}
|
2011-09-08 00:36:56 +00:00
|
|
|
return MakeIsLessOrEqualCstCt(var, value, boolvar);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-09-08 00:36:56 +00:00
|
|
|
if (value == var->Max()) {
|
2017-12-08 14:52:49 +01:00
|
|
|
if (CapSub(var->Max(), var->Min()) == 1) {
|
2012-09-03 21:40:45 +00:00
|
|
|
return MakeEquality(MakeSum(var, -value + 1), boolvar);
|
|
|
|
|
}
|
2011-09-08 00:36:56 +00:00
|
|
|
return MakeIsGreaterOrEqualCstCt(var, value, boolvar);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2012-06-08 16:49:21 +00:00
|
|
|
if (boolvar->Bound()) {
|
|
|
|
|
if (boolvar->Min() == 0) {
|
|
|
|
|
return MakeNonEquality(var, value);
|
|
|
|
|
} else {
|
|
|
|
|
return MakeEquality(var, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
// TODO(user) : what happens if the constraint is not posted?
|
|
|
|
|
// The cache becomes tainted.
|
2012-07-05 16:27:57 +00:00
|
|
|
model_cache_->InsertExprConstantExpression(
|
2013-10-10 15:23:20 +00:00
|
|
|
boolvar, var, value, ModelCache::EXPR_CONSTANT_IS_EQUAL);
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* left = nullptr;
|
|
|
|
|
IntExpr* right = nullptr;
|
2012-07-06 14:05:06 +00:00
|
|
|
if (IsADifference(var, &left, &right)) {
|
|
|
|
|
return MakeIsEqualCt(left, MakeSum(right, value), boolvar);
|
2012-06-29 09:41:24 +00:00
|
|
|
} else {
|
2012-07-05 16:27:57 +00:00
|
|
|
return RevAlloc(new IsEqualCstCt(this, var->Var(), value, boolvar));
|
2012-06-29 09:41:24 +00:00
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----- is_diff_cst Constraint -----
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2011-12-17 21:20:58 +00:00
|
|
|
class IsDiffCstCt : public CastConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2021-04-01 12:13:35 +02:00
|
|
|
IsDiffCstCt(Solver* const s, IntVar* const v, int64_t c, IntVar* const b)
|
2013-10-10 15:23:20 +00:00
|
|
|
: CastConstraint(s, b), var_(v), cst_(c), demon_(nullptr) {}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
demon_ = solver()->MakeConstraintInitialPropagateCallback(this);
|
|
|
|
|
var_->WhenDomain(demon_);
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_->WhenBound(demon_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
bool inhibit = var_->Bound();
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t l = 1 - var_->Contains(cst_);
|
|
|
|
|
int64_t u = inhibit ? l : 1;
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_->SetRange(l, u);
|
|
|
|
|
if (target_var_->Bound()) {
|
|
|
|
|
if (target_var_->Min() == 1) {
|
2017-02-07 17:44:35 +01:00
|
|
|
if (var_->Size() <= 0xFFFFFF) {
|
|
|
|
|
var_->RemoveValue(cst_);
|
|
|
|
|
inhibit = true;
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
} else {
|
|
|
|
|
var_->SetValue(cst_);
|
2017-02-07 17:44:35 +01:00
|
|
|
inhibit = true;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (inhibit) {
|
|
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("IsDiffCstCt(%s, %d, %s)", var_->DebugString(), cst_,
|
2018-10-31 16:18:18 +01:00
|
|
|
target_var_->DebugString());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kIsDifferent, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
2011-07-11 20:13:14 +00:00
|
|
|
var_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, cst_);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kIsDifferent, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst_;
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* demon_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
IntVar* Solver::MakeIsDifferentCstVar(IntExpr* const var, int64_t value) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* left = nullptr;
|
|
|
|
|
IntExpr* right = nullptr;
|
2012-07-06 14:05:06 +00:00
|
|
|
if (IsADifference(var, &left, &right)) {
|
|
|
|
|
return MakeIsDifferentVar(left, MakeSum(right, value));
|
2012-06-29 09:41:24 +00:00
|
|
|
}
|
2012-07-05 16:27:57 +00:00
|
|
|
return var->Var()->IsDifferent(value);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeIsDifferentCstCt(IntExpr* const var, int64_t value,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const boolvar) {
|
2011-09-08 00:36:56 +00:00
|
|
|
CHECK_EQ(this, var->solver());
|
|
|
|
|
CHECK_EQ(this, boolvar->solver());
|
|
|
|
|
if (value == var->Min()) {
|
|
|
|
|
return MakeIsGreaterOrEqualCstCt(var, value + 1, boolvar);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-09-08 00:36:56 +00:00
|
|
|
if (value == var->Max()) {
|
|
|
|
|
return MakeIsLessOrEqualCstCt(var, value - 1, boolvar);
|
|
|
|
|
}
|
2012-07-05 16:27:57 +00:00
|
|
|
if (var->IsVar() && !var->Var()->Contains(value)) {
|
2021-04-01 12:13:35 +02:00
|
|
|
return MakeEquality(boolvar, int64_t{1});
|
2012-06-16 16:26:06 +00:00
|
|
|
}
|
2012-07-05 16:27:57 +00:00
|
|
|
if (var->Bound() && var->Min() == value) {
|
2012-06-16 16:26:06 +00:00
|
|
|
return MakeEquality(boolvar, Zero());
|
|
|
|
|
}
|
2012-06-08 16:49:21 +00:00
|
|
|
if (boolvar->Bound()) {
|
|
|
|
|
if (boolvar->Min() == 0) {
|
|
|
|
|
return MakeEquality(var, value);
|
|
|
|
|
} else {
|
|
|
|
|
return MakeNonEquality(var, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-05 16:27:57 +00:00
|
|
|
model_cache_->InsertExprConstantExpression(
|
2013-10-10 15:23:20 +00:00
|
|
|
boolvar, var, value, ModelCache::EXPR_CONSTANT_IS_NOT_EQUAL);
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* left = nullptr;
|
|
|
|
|
IntExpr* right = nullptr;
|
2012-07-06 14:05:06 +00:00
|
|
|
if (IsADifference(var, &left, &right)) {
|
|
|
|
|
return MakeIsDifferentCt(left, MakeSum(right, value), boolvar);
|
2012-06-29 09:41:24 +00:00
|
|
|
} else {
|
2012-07-05 16:27:57 +00:00
|
|
|
return RevAlloc(new IsDiffCstCt(this, var->Var(), value, boolvar));
|
2012-06-29 09:41:24 +00:00
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----- is_greater_equal_cst Constraint -----
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2011-12-17 21:20:58 +00:00
|
|
|
class IsGreaterEqualCstCt : public CastConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2021-04-01 12:13:35 +02:00
|
|
|
IsGreaterEqualCstCt(Solver* const s, IntExpr* const v, int64_t c,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const b)
|
2013-10-10 15:23:20 +00:00
|
|
|
: CastConstraint(s, b), expr_(v), cst_(c), demon_(nullptr) {}
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
demon_ = solver()->MakeConstraintInitialPropagateCallback(this);
|
2013-07-22 18:24:22 +00:00
|
|
|
expr_->WhenRange(demon_);
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_->WhenBound(demon_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
bool inhibit = false;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t u = expr_->Max() >= cst_;
|
|
|
|
|
int64_t l = expr_->Min() >= cst_;
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_->SetRange(l, u);
|
|
|
|
|
if (target_var_->Bound()) {
|
2010-09-15 12:42:33 +00:00
|
|
|
inhibit = true;
|
2011-12-17 21:20:58 +00:00
|
|
|
if (target_var_->Min() == 0) {
|
2013-07-22 18:24:22 +00:00
|
|
|
expr_->SetMax(cst_ - 1);
|
2010-09-15 12:42:33 +00:00
|
|
|
} else {
|
2013-07-22 18:24:22 +00:00
|
|
|
expr_->SetMin(cst_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
if (inhibit && ((target_var_->Max() == 0 && expr_->Max() < cst_) ||
|
|
|
|
|
(target_var_->Min() == 1 && expr_->Min() >= cst_))) {
|
|
|
|
|
// Can we safely inhibit? Sometimes an expression is not
|
|
|
|
|
// persistent, just monotonic.
|
2010-09-15 12:42:33 +00:00
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("IsGreaterEqualCstCt(%s, %d, %s)",
|
2018-10-31 16:18:18 +01:00
|
|
|
expr_->DebugString(), cst_,
|
|
|
|
|
target_var_->DebugString());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kIsGreaterOrEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
2013-07-22 18:24:22 +00:00
|
|
|
expr_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, cst_);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kIsGreaterOrEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const expr_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst_;
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* demon_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
IntVar* Solver::MakeIsGreaterOrEqualCstVar(IntExpr* const var, int64_t value) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (var->Min() >= value) {
|
2021-04-01 12:13:35 +02:00
|
|
|
return MakeIntConst(int64_t{1});
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
if (var->Max() < value) {
|
2021-04-01 12:13:35 +02:00
|
|
|
return MakeIntConst(int64_t{0});
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2012-07-05 16:27:57 +00:00
|
|
|
if (var->IsVar()) {
|
|
|
|
|
return var->Var()->IsGreaterOrEqual(value);
|
|
|
|
|
} else {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const boolvar =
|
2018-12-03 14:26:31 +01:00
|
|
|
MakeBoolVar(absl::StrFormat("Is(%s >= %d)", var->DebugString(), value));
|
2012-07-05 16:27:57 +00:00
|
|
|
AddConstraint(MakeIsGreaterOrEqualCstCt(var, value, boolvar));
|
|
|
|
|
return boolvar;
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
IntVar* Solver::MakeIsGreaterCstVar(IntExpr* const var, int64_t value) {
|
2011-09-08 00:36:56 +00:00
|
|
|
return MakeIsGreaterOrEqualCstVar(var, value + 1);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeIsGreaterOrEqualCstCt(IntExpr* const var, int64_t value,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const boolvar) {
|
2012-06-11 12:46:09 +00:00
|
|
|
if (boolvar->Bound()) {
|
|
|
|
|
if (boolvar->Min() == 0) {
|
|
|
|
|
return MakeLess(var, value);
|
|
|
|
|
} else {
|
|
|
|
|
return MakeGreaterOrEqual(var, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-08 00:36:56 +00:00
|
|
|
CHECK_EQ(this, var->solver());
|
|
|
|
|
CHECK_EQ(this, boolvar->solver());
|
2012-07-05 16:27:57 +00:00
|
|
|
model_cache_->InsertExprConstantExpression(
|
2013-10-10 15:23:20 +00:00
|
|
|
boolvar, var, value, ModelCache::EXPR_CONSTANT_IS_GREATER_OR_EQUAL);
|
2011-09-08 00:36:56 +00:00
|
|
|
return RevAlloc(new IsGreaterEqualCstCt(this, var, value, boolvar));
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeIsGreaterCstCt(IntExpr* const v, int64_t c,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const b) {
|
2010-09-15 12:42:33 +00:00
|
|
|
return MakeIsGreaterOrEqualCstCt(v, c + 1, b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----- is_lesser_equal_cst Constraint -----
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2011-12-17 21:20:58 +00:00
|
|
|
class IsLessEqualCstCt : public CastConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2021-04-01 12:13:35 +02:00
|
|
|
IsLessEqualCstCt(Solver* const s, IntExpr* const v, int64_t c,
|
|
|
|
|
IntVar* const b)
|
2013-10-10 15:23:20 +00:00
|
|
|
: CastConstraint(s, b), expr_(v), cst_(c), demon_(nullptr) {}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
demon_ = solver()->MakeConstraintInitialPropagateCallback(this);
|
2013-07-22 18:24:22 +00:00
|
|
|
expr_->WhenRange(demon_);
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_->WhenBound(demon_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
bool inhibit = false;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t u = expr_->Min() <= cst_;
|
|
|
|
|
int64_t l = expr_->Max() <= cst_;
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_->SetRange(l, u);
|
|
|
|
|
if (target_var_->Bound()) {
|
2010-09-15 12:42:33 +00:00
|
|
|
inhibit = true;
|
2011-12-17 21:20:58 +00:00
|
|
|
if (target_var_->Min() == 0) {
|
2013-07-22 18:24:22 +00:00
|
|
|
expr_->SetMin(cst_ + 1);
|
2010-09-15 12:42:33 +00:00
|
|
|
} else {
|
2013-07-22 18:24:22 +00:00
|
|
|
expr_->SetMax(cst_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
if (inhibit && ((target_var_->Max() == 0 && expr_->Min() > cst_) ||
|
|
|
|
|
(target_var_->Min() == 1 && expr_->Max() <= cst_))) {
|
|
|
|
|
// Can we safely inhibit? Sometimes an expression is not
|
|
|
|
|
// persistent, just monotonic.
|
2010-09-15 12:42:33 +00:00
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("IsLessEqualCstCt(%s, %d, %s)", expr_->DebugString(),
|
|
|
|
|
cst_, target_var_->DebugString());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kIsLessOrEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
2013-07-22 18:24:22 +00:00
|
|
|
expr_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, cst_);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
2011-12-17 21:20:58 +00:00
|
|
|
target_var_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kIsLessOrEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const expr_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst_;
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* demon_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
IntVar* Solver::MakeIsLessOrEqualCstVar(IntExpr* const var, int64_t value) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (var->Max() <= value) {
|
2021-04-01 12:13:35 +02:00
|
|
|
return MakeIntConst(int64_t{1});
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
if (var->Min() > value) {
|
2021-04-01 12:13:35 +02:00
|
|
|
return MakeIntConst(int64_t{0});
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2012-07-05 16:27:57 +00:00
|
|
|
if (var->IsVar()) {
|
|
|
|
|
return var->Var()->IsLessOrEqual(value);
|
|
|
|
|
} else {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const boolvar =
|
2018-12-03 14:26:31 +01:00
|
|
|
MakeBoolVar(absl::StrFormat("Is(%s <= %d)", var->DebugString(), value));
|
2012-07-05 16:27:57 +00:00
|
|
|
AddConstraint(MakeIsLessOrEqualCstCt(var, value, boolvar));
|
|
|
|
|
return boolvar;
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
IntVar* Solver::MakeIsLessCstVar(IntExpr* const var, int64_t value) {
|
2010-09-15 12:42:33 +00:00
|
|
|
return MakeIsLessOrEqualCstVar(var, value - 1);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeIsLessOrEqualCstCt(IntExpr* const var, int64_t value,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const boolvar) {
|
2012-06-11 12:46:09 +00:00
|
|
|
if (boolvar->Bound()) {
|
|
|
|
|
if (boolvar->Min() == 0) {
|
|
|
|
|
return MakeGreater(var, value);
|
|
|
|
|
} else {
|
|
|
|
|
return MakeLessOrEqual(var, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-08 00:36:56 +00:00
|
|
|
CHECK_EQ(this, var->solver());
|
|
|
|
|
CHECK_EQ(this, boolvar->solver());
|
2012-07-05 16:27:57 +00:00
|
|
|
model_cache_->InsertExprConstantExpression(
|
2013-10-10 15:23:20 +00:00
|
|
|
boolvar, var, value, ModelCache::EXPR_CONSTANT_IS_LESS_OR_EQUAL);
|
2011-09-08 00:36:56 +00:00
|
|
|
return RevAlloc(new IsLessEqualCstCt(this, var, value, boolvar));
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeIsLessCstCt(IntExpr* const v, int64_t c,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const b) {
|
2010-09-15 12:42:33 +00:00
|
|
|
return MakeIsLessOrEqualCstCt(v, c - 1, b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----- BetweenCt -----
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2010-09-15 12:42:33 +00:00
|
|
|
class BetweenCt : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2021-04-01 12:13:35 +02:00
|
|
|
BetweenCt(Solver* const s, IntExpr* const v, int64_t l, int64_t u)
|
2014-05-25 16:33:40 +00:00
|
|
|
: Constraint(s), expr_(v), min_(l), max_(u), demon_(nullptr) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2014-05-12 19:28:11 +00:00
|
|
|
if (!expr_->IsVar()) {
|
2014-05-25 16:33:40 +00:00
|
|
|
demon_ = solver()->MakeConstraintInitialPropagateCallback(this);
|
|
|
|
|
expr_->WhenRange(demon_);
|
2014-05-12 19:28:11 +00:00
|
|
|
}
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2014-05-25 16:33:40 +00:00
|
|
|
expr_->SetRange(min_, max_);
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t emin = 0;
|
|
|
|
|
int64_t emax = 0;
|
2014-06-11 22:16:31 +00:00
|
|
|
expr_->Range(&emin, &emax);
|
|
|
|
|
if (demon_ != nullptr && emin >= min_ && emax <= max_) {
|
2014-05-25 16:33:40 +00:00
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("BetweenCt(%s, %d, %d)", expr_->DebugString(), min_,
|
|
|
|
|
max_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kBetween, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kMinArgument, min_);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
2014-05-12 19:28:11 +00:00
|
|
|
expr_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kMaxArgument, max_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kBetween, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const expr_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t min_;
|
|
|
|
|
int64_t max_;
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* demon_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
2014-11-07 14:30:53 +00:00
|
|
|
|
2016-08-05 15:38:20 +02:00
|
|
|
// ----- NonMember constraint -----
|
|
|
|
|
|
2016-07-25 11:09:39 -07:00
|
|
|
class NotBetweenCt : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2021-04-01 12:13:35 +02:00
|
|
|
NotBetweenCt(Solver* const s, IntExpr* const v, int64_t l, int64_t u)
|
2016-07-25 11:09:39 -07:00
|
|
|
: Constraint(s), expr_(v), min_(l), max_(u), demon_(nullptr) {}
|
|
|
|
|
|
|
|
|
|
void Post() override {
|
2016-07-29 11:04:36 -07:00
|
|
|
demon_ = solver()->MakeConstraintInitialPropagateCallback(this);
|
|
|
|
|
expr_->WhenRange(demon_);
|
2016-07-25 11:09:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InitialPropagate() override {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t emin = 0;
|
|
|
|
|
int64_t emax = 0;
|
2016-07-25 11:09:39 -07:00
|
|
|
expr_->Range(&emin, &emax);
|
|
|
|
|
if (emin >= min_) {
|
|
|
|
|
expr_->SetMin(max_ + 1);
|
|
|
|
|
} else if (emax <= max_) {
|
|
|
|
|
expr_->SetMax(min_ - 1);
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-29 11:04:36 -07:00
|
|
|
if (!expr_->IsVar() && (emax < min_ || emin > max_)) {
|
2016-07-25 11:09:39 -07:00
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string DebugString() const override {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("NotBetweenCt(%s, %d, %d)", expr_->DebugString(),
|
|
|
|
|
min_, max_);
|
2016-07-25 11:09:39 -07:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2016-07-25 11:09:39 -07:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kNotBetween, this);
|
|
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kMinArgument, min_);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
|
|
|
|
expr_);
|
|
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kMaxArgument, max_);
|
|
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kBetween, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const expr_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t min_;
|
|
|
|
|
int64_t max_;
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* demon_;
|
2016-07-25 11:09:39 -07:00
|
|
|
};
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t ExtractExprProductCoeff(IntExpr** expr) {
|
|
|
|
|
int64_t prod = 1;
|
|
|
|
|
int64_t coeff = 1;
|
2020-10-22 23:36:58 +02:00
|
|
|
while ((*expr)->solver()->IsProduct(*expr, expr, &coeff)) prod *= coeff;
|
2014-11-07 14:30:53 +00:00
|
|
|
return prod;
|
|
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeBetweenCt(IntExpr* expr, int64_t l, int64_t u) {
|
2017-12-08 14:52:49 +01:00
|
|
|
DCHECK_EQ(this, expr->solver());
|
2014-11-07 14:30:53 +00:00
|
|
|
// Catch empty and singleton intervals.
|
|
|
|
|
if (l >= u) {
|
2020-10-22 23:36:58 +02:00
|
|
|
if (l > u) return MakeFalseConstraint();
|
2017-12-08 14:52:49 +01:00
|
|
|
return MakeEquality(expr, l);
|
2014-11-07 14:30:53 +00:00
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t emin = 0;
|
|
|
|
|
int64_t emax = 0;
|
2017-12-08 14:52:49 +01:00
|
|
|
expr->Range(&emin, &emax);
|
2014-11-07 14:30:53 +00:00
|
|
|
// Catch the trivial cases first.
|
2020-10-22 23:36:58 +02:00
|
|
|
if (emax < l || emin > u) return MakeFalseConstraint();
|
|
|
|
|
if (emin >= l && emax <= u) return MakeTrueConstraint();
|
2014-11-07 14:30:53 +00:00
|
|
|
// Catch one-sided constraints.
|
2020-10-22 23:36:58 +02:00
|
|
|
if (emax <= u) return MakeGreaterOrEqual(expr, l);
|
|
|
|
|
if (emin >= l) return MakeLessOrEqual(expr, u);
|
2014-11-07 14:30:53 +00:00
|
|
|
// Simplify the common factor, if any.
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t coeff = ExtractExprProductCoeff(&expr);
|
2014-11-07 14:30:53 +00:00
|
|
|
if (coeff != 1) {
|
2020-10-22 23:36:58 +02:00
|
|
|
CHECK_NE(coeff, 0); // Would have been caught by the trivial cases already.
|
2014-11-07 14:30:53 +00:00
|
|
|
if (coeff < 0) {
|
|
|
|
|
std::swap(u, l);
|
|
|
|
|
u = -u;
|
|
|
|
|
l = -l;
|
|
|
|
|
coeff = -coeff;
|
2014-05-25 01:14:59 +00:00
|
|
|
}
|
2017-12-08 14:52:49 +01:00
|
|
|
return MakeBetweenCt(expr, PosIntDivUp(l, coeff), PosIntDivDown(u, coeff));
|
2012-09-03 19:39:43 +00:00
|
|
|
} else {
|
2014-11-07 14:30:53 +00:00
|
|
|
// No further reduction is possible.
|
2017-12-08 14:52:49 +01:00
|
|
|
return RevAlloc(new BetweenCt(this, expr, l, u));
|
2012-09-03 19:39:43 +00:00
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeNotBetweenCt(IntExpr* expr, int64_t l, int64_t u) {
|
2017-12-08 14:52:49 +01:00
|
|
|
DCHECK_EQ(this, expr->solver());
|
2016-07-25 11:09:39 -07:00
|
|
|
// Catch empty interval.
|
|
|
|
|
if (l > u) {
|
|
|
|
|
return MakeTrueConstraint();
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t emin = 0;
|
|
|
|
|
int64_t emax = 0;
|
2017-12-08 14:52:49 +01:00
|
|
|
expr->Range(&emin, &emax);
|
2016-07-25 11:09:39 -07:00
|
|
|
// Catch the trivial cases first.
|
2020-10-22 23:36:58 +02:00
|
|
|
if (emax < l || emin > u) return MakeTrueConstraint();
|
|
|
|
|
if (emin >= l && emax <= u) return MakeFalseConstraint();
|
2016-07-25 11:09:39 -07:00
|
|
|
// Catch one-sided constraints.
|
2020-10-22 23:36:58 +02:00
|
|
|
if (emin >= l) return MakeGreater(expr, u);
|
|
|
|
|
if (emax <= u) return MakeLess(expr, l);
|
2017-12-08 14:52:49 +01:00
|
|
|
// TODO(user): Add back simplification code if expr is constant *
|
|
|
|
|
// other_expr.
|
|
|
|
|
return RevAlloc(new NotBetweenCt(this, expr, l, u));
|
2016-07-25 11:09:39 -07:00
|
|
|
}
|
|
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
// ----- is_between_cst Constraint -----
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2010-09-15 12:42:33 +00:00
|
|
|
class IsBetweenCt : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2021-04-01 12:13:35 +02:00
|
|
|
IsBetweenCt(Solver* const s, IntExpr* const e, int64_t l, int64_t u,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const b)
|
2020-10-22 23:36:58 +02:00
|
|
|
: Constraint(s),
|
|
|
|
|
expr_(e),
|
|
|
|
|
min_(l),
|
|
|
|
|
max_(u),
|
|
|
|
|
boolvar_(b),
|
2013-10-10 15:23:20 +00:00
|
|
|
demon_(nullptr) {}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
demon_ = solver()->MakeConstraintInitialPropagateCallback(this);
|
2014-05-12 19:28:11 +00:00
|
|
|
expr_->WhenRange(demon_);
|
2010-09-15 12:42:33 +00:00
|
|
|
boolvar_->WhenBound(demon_);
|
|
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
bool inhibit = false;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t emin = 0;
|
|
|
|
|
int64_t emax = 0;
|
2014-05-15 09:19:50 +00:00
|
|
|
expr_->Range(&emin, &emax);
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t u = 1 - (emin > max_ || emax < min_);
|
|
|
|
|
int64_t l = emax <= max_ && emin >= min_;
|
2010-09-15 12:42:33 +00:00
|
|
|
boolvar_->SetRange(l, u);
|
|
|
|
|
if (boolvar_->Bound()) {
|
|
|
|
|
inhibit = true;
|
|
|
|
|
if (boolvar_->Min() == 0) {
|
2014-05-12 19:28:11 +00:00
|
|
|
if (expr_->IsVar()) {
|
|
|
|
|
expr_->Var()->RemoveInterval(min_, max_);
|
2014-05-15 09:19:50 +00:00
|
|
|
inhibit = true;
|
2014-05-12 19:28:11 +00:00
|
|
|
} else if (emin > min_) {
|
|
|
|
|
expr_->SetMin(max_ + 1);
|
|
|
|
|
} else if (emax < max_) {
|
|
|
|
|
expr_->SetMax(min_ - 1);
|
|
|
|
|
}
|
2014-05-13 13:20:13 +00:00
|
|
|
} else {
|
|
|
|
|
expr_->SetRange(min_, max_);
|
|
|
|
|
inhibit = true;
|
2014-05-12 19:28:11 +00:00
|
|
|
}
|
|
|
|
|
if (inhibit && expr_->IsVar()) {
|
|
|
|
|
demon_->inhibit(solver());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("IsBetweenCt(%s, %d, %d, %s)", expr_->DebugString(),
|
|
|
|
|
min_, max_, boolvar_->DebugString());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kIsBetween, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kMinArgument, min_);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
2014-05-12 19:28:11 +00:00
|
|
|
expr_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kMaxArgument, max_);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
2011-07-11 20:13:14 +00:00
|
|
|
boolvar_);
|
|
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kIsBetween, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const expr_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t min_;
|
|
|
|
|
int64_t max_;
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const boolvar_;
|
|
|
|
|
Demon* demon_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2014-11-07 14:30:53 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeIsBetweenCt(IntExpr* expr, int64_t l, int64_t u,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const b) {
|
2017-12-08 14:52:49 +01:00
|
|
|
CHECK_EQ(this, expr->solver());
|
2010-09-15 12:42:33 +00:00
|
|
|
CHECK_EQ(this, b->solver());
|
2014-11-07 14:30:53 +00:00
|
|
|
// Catch empty and singleton intervals.
|
|
|
|
|
if (l >= u) {
|
2020-10-22 23:36:58 +02:00
|
|
|
if (l > u) return MakeEquality(b, Zero());
|
2017-12-08 14:52:49 +01:00
|
|
|
return MakeIsEqualCstCt(expr, l, b);
|
2014-11-07 14:30:53 +00:00
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t emin = 0;
|
|
|
|
|
int64_t emax = 0;
|
2017-12-08 14:52:49 +01:00
|
|
|
expr->Range(&emin, &emax);
|
2014-11-07 14:30:53 +00:00
|
|
|
// Catch the trivial cases first.
|
2020-10-22 23:36:58 +02:00
|
|
|
if (emax < l || emin > u) return MakeEquality(b, Zero());
|
|
|
|
|
if (emin >= l && emax <= u) return MakeEquality(b, 1);
|
2014-11-07 14:30:53 +00:00
|
|
|
// Catch one-sided constraints.
|
2020-10-22 23:36:58 +02:00
|
|
|
if (emax <= u) return MakeIsGreaterOrEqualCstCt(expr, l, b);
|
|
|
|
|
if (emin >= l) return MakeIsLessOrEqualCstCt(expr, u, b);
|
2014-11-07 14:30:53 +00:00
|
|
|
// Simplify the common factor, if any.
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t coeff = ExtractExprProductCoeff(&expr);
|
2014-11-07 14:30:53 +00:00
|
|
|
if (coeff != 1) {
|
2020-10-22 23:36:58 +02:00
|
|
|
CHECK_NE(coeff, 0); // Would have been caught by the trivial cases already.
|
2014-11-07 14:30:53 +00:00
|
|
|
if (coeff < 0) {
|
|
|
|
|
std::swap(u, l);
|
|
|
|
|
u = -u;
|
|
|
|
|
l = -l;
|
|
|
|
|
coeff = -coeff;
|
2014-05-25 01:14:59 +00:00
|
|
|
}
|
2017-12-08 14:52:49 +01:00
|
|
|
return MakeIsBetweenCt(expr, PosIntDivUp(l, coeff), PosIntDivDown(u, coeff),
|
2014-11-07 14:30:53 +00:00
|
|
|
b);
|
|
|
|
|
} else {
|
|
|
|
|
// No further reduction is possible.
|
2017-12-08 14:52:49 +01:00
|
|
|
return RevAlloc(new IsBetweenCt(this, expr, l, u, b));
|
2014-05-25 01:14:59 +00:00
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
IntVar* Solver::MakeIsBetweenVar(IntExpr* const v, int64_t l, int64_t u) {
|
2013-03-19 18:29:15 +00:00
|
|
|
CHECK_EQ(this, v->solver());
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const b = MakeBoolVar();
|
2013-03-19 18:29:15 +00:00
|
|
|
AddConstraint(MakeIsBetweenCt(v, l, u, b));
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
// ---------- Member ----------
|
|
|
|
|
|
|
|
|
|
// ----- Member(IntVar, IntSet) -----
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2014-05-12 19:28:11 +00:00
|
|
|
// TODO(user): Do not create holes on expressions.
|
2010-09-15 12:42:33 +00:00
|
|
|
class MemberCt : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
MemberCt(Solver* const s, IntVar* const v,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& sorted_values)
|
2011-05-20 14:01:28 +00:00
|
|
|
: Constraint(s), var_(v), values_(sorted_values) {
|
2013-10-10 15:23:20 +00:00
|
|
|
DCHECK(v != nullptr);
|
|
|
|
|
DCHECK(s != nullptr);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override { var_->SetValues(values_); }
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("Member(%s, %s)", var_->DebugString(),
|
|
|
|
|
absl::StrJoin(values_, ", "));
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kMember, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
2011-07-11 20:13:14 +00:00
|
|
|
var_);
|
2013-10-10 15:23:20 +00:00
|
|
|
visitor->VisitIntegerArrayArgument(ModelVisitor::kValuesArgument, values_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kMember, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var_;
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t> values_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
2014-05-24 01:57:45 +00:00
|
|
|
|
|
|
|
|
class NotMemberCt : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
NotMemberCt(Solver* const s, IntVar* const v,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& sorted_values)
|
2014-05-24 01:57:45 +00:00
|
|
|
: Constraint(s), var_(v), values_(sorted_values) {
|
|
|
|
|
DCHECK(v != nullptr);
|
|
|
|
|
DCHECK(s != nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {}
|
2014-05-24 01:57:45 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override { var_->RemoveValues(values_); }
|
2014-05-24 01:57:45 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("NotMember(%s, %s)", var_->DebugString(),
|
|
|
|
|
absl::StrJoin(values_, ", "));
|
2014-05-24 01:57:45 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2014-05-24 01:57:45 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kMember, this);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
|
|
|
|
var_);
|
|
|
|
|
visitor->VisitIntegerArrayArgument(ModelVisitor::kValuesArgument, values_);
|
|
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kMember, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var_;
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t> values_;
|
2014-05-24 01:57:45 +00:00
|
|
|
};
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeMemberCt(IntExpr* expr,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& values) {
|
|
|
|
|
const int64_t coeff = ExtractExprProductCoeff(&expr);
|
2014-11-07 14:30:53 +00:00
|
|
|
if (coeff == 0) {
|
|
|
|
|
return std::find(values.begin(), values.end(), 0) == values.end()
|
2016-09-12 13:40:24 +02:00
|
|
|
? MakeFalseConstraint()
|
|
|
|
|
: MakeTrueConstraint();
|
2014-11-07 14:30:53 +00:00
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> copied_values = values;
|
2014-11-07 14:30:53 +00:00
|
|
|
// If the expression is a non-trivial product, we filter out the values that
|
|
|
|
|
// aren't multiples of "coeff", and divide them.
|
|
|
|
|
if (coeff != 1) {
|
|
|
|
|
int num_kept = 0;
|
2021-04-01 12:13:35 +02:00
|
|
|
for (const int64_t v : copied_values) {
|
2020-10-22 23:36:58 +02:00
|
|
|
if (v % coeff == 0) copied_values[num_kept++] = v / coeff;
|
2014-05-25 01:28:47 +00:00
|
|
|
}
|
2014-11-07 14:30:53 +00:00
|
|
|
copied_values.resize(num_kept);
|
|
|
|
|
}
|
|
|
|
|
// Filter out the values that are outside the [Min, Max] interval.
|
2016-08-05 15:38:20 +02:00
|
|
|
int num_kept = 0;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t emin;
|
|
|
|
|
int64_t emax;
|
2016-08-05 15:38:20 +02:00
|
|
|
expr->Range(&emin, &emax);
|
2021-04-01 12:13:35 +02:00
|
|
|
for (const int64_t v : copied_values) {
|
2020-10-22 23:36:58 +02:00
|
|
|
if (v >= emin && v <= emax) copied_values[num_kept++] = v;
|
2016-08-05 15:38:20 +02:00
|
|
|
}
|
|
|
|
|
copied_values.resize(num_kept);
|
2016-07-29 14:30:14 -07:00
|
|
|
// Catch empty set.
|
2020-10-22 23:36:58 +02:00
|
|
|
if (copied_values.empty()) return MakeFalseConstraint();
|
2014-11-07 14:30:53 +00:00
|
|
|
// Sort and remove duplicates.
|
2018-04-11 13:00:30 +02:00
|
|
|
gtl::STLSortAndRemoveDuplicates(&copied_values);
|
2016-07-29 14:30:14 -07:00
|
|
|
// Special case for singleton.
|
2020-10-22 23:36:58 +02:00
|
|
|
if (copied_values.size() == 1) return MakeEquality(expr, copied_values[0]);
|
2014-11-07 14:30:53 +00:00
|
|
|
// Catch contiguous intervals.
|
|
|
|
|
if (copied_values.size() ==
|
|
|
|
|
copied_values.back() - copied_values.front() + 1) {
|
|
|
|
|
// Note: MakeBetweenCt() has a fast-track for trivially true constraints.
|
|
|
|
|
return MakeBetweenCt(expr, copied_values.front(), copied_values.back());
|
|
|
|
|
}
|
|
|
|
|
// If the set of values in [expr.Min(), expr.Max()] that are *not* in
|
|
|
|
|
// "values" is smaller than "values", then it's more efficient to use
|
|
|
|
|
// NotMemberCt. Catch that case here.
|
2016-08-05 15:38:20 +02:00
|
|
|
if (emax - emin < 2 * copied_values.size()) {
|
2014-11-07 14:30:53 +00:00
|
|
|
// Convert "copied_values" to list the values *not* allowed.
|
2016-08-05 15:38:20 +02:00
|
|
|
std::vector<bool> is_among_input_values(emax - emin + 1, false);
|
2021-04-01 12:13:35 +02:00
|
|
|
for (const int64_t v : copied_values)
|
|
|
|
|
is_among_input_values[v - emin] = true;
|
2016-08-05 15:38:20 +02:00
|
|
|
// We use the zero valued indices of is_among_input_values to build the
|
|
|
|
|
// complement of copied_values.
|
2014-11-07 14:30:53 +00:00
|
|
|
copied_values.clear();
|
2021-04-01 12:13:35 +02:00
|
|
|
for (int64_t v_off = 0; v_off < is_among_input_values.size(); ++v_off) {
|
2020-10-22 23:36:58 +02:00
|
|
|
if (!is_among_input_values[v_off]) copied_values.push_back(v_off + emin);
|
2014-11-07 14:30:53 +00:00
|
|
|
}
|
|
|
|
|
// The empty' case (all values in range [expr.Min(), expr.Max()] are in the
|
|
|
|
|
// "values" input) was caught earlier, by the "contiguous interval" case.
|
|
|
|
|
DCHECK_GE(copied_values.size(), 1);
|
|
|
|
|
if (copied_values.size() == 1) {
|
|
|
|
|
return MakeNonEquality(expr, copied_values[0]);
|
2014-05-24 01:57:45 +00:00
|
|
|
}
|
2014-11-07 14:30:53 +00:00
|
|
|
return RevAlloc(new NotMemberCt(this, expr->Var(), copied_values));
|
2012-09-03 19:39:43 +00:00
|
|
|
}
|
2014-11-07 14:30:53 +00:00
|
|
|
// Otherwise, just use MemberCt. No further reduction is possible.
|
|
|
|
|
return RevAlloc(new MemberCt(this, expr->Var(), copied_values));
|
2011-05-20 14:01:28 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeMemberCt(IntExpr* const expr,
|
|
|
|
|
const std::vector<int>& values) {
|
2014-05-25 01:28:47 +00:00
|
|
|
return MakeMemberCt(expr, ToInt64Vector(values));
|
2011-05-20 14:01:28 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeNotMemberCt(IntExpr* expr,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& values) {
|
|
|
|
|
const int64_t coeff = ExtractExprProductCoeff(&expr);
|
2016-07-25 11:09:39 -07:00
|
|
|
if (coeff == 0) {
|
|
|
|
|
return std::find(values.begin(), values.end(), 0) == values.end()
|
2016-09-12 13:40:24 +02:00
|
|
|
? MakeTrueConstraint()
|
|
|
|
|
: MakeFalseConstraint();
|
2016-07-25 11:09:39 -07:00
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> copied_values = values;
|
2016-07-25 11:09:39 -07:00
|
|
|
// If the expression is a non-trivial product, we filter out the values that
|
|
|
|
|
// aren't multiples of "coeff", and divide them.
|
|
|
|
|
if (coeff != 1) {
|
|
|
|
|
int num_kept = 0;
|
2021-04-01 12:13:35 +02:00
|
|
|
for (const int64_t v : copied_values) {
|
2020-10-22 23:36:58 +02:00
|
|
|
if (v % coeff == 0) copied_values[num_kept++] = v / coeff;
|
2016-07-25 11:09:39 -07:00
|
|
|
}
|
|
|
|
|
copied_values.resize(num_kept);
|
|
|
|
|
}
|
|
|
|
|
// Filter out the values that are outside the [Min, Max] interval.
|
2016-08-05 15:38:20 +02:00
|
|
|
int num_kept = 0;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t emin;
|
|
|
|
|
int64_t emax;
|
2016-08-05 15:38:20 +02:00
|
|
|
expr->Range(&emin, &emax);
|
2021-04-01 12:13:35 +02:00
|
|
|
for (const int64_t v : copied_values) {
|
2020-10-22 23:36:58 +02:00
|
|
|
if (v >= emin && v <= emax) copied_values[num_kept++] = v;
|
2016-08-05 15:38:20 +02:00
|
|
|
}
|
|
|
|
|
copied_values.resize(num_kept);
|
2016-07-29 14:30:14 -07:00
|
|
|
// Catch empty set.
|
2020-10-22 23:36:58 +02:00
|
|
|
if (copied_values.empty()) return MakeTrueConstraint();
|
2016-07-25 11:09:39 -07:00
|
|
|
// Sort and remove duplicates.
|
2018-04-11 13:00:30 +02:00
|
|
|
gtl::STLSortAndRemoveDuplicates(&copied_values);
|
2016-07-29 14:30:14 -07:00
|
|
|
// Special case for singleton.
|
2020-10-22 23:36:58 +02:00
|
|
|
if (copied_values.size() == 1) return MakeNonEquality(expr, copied_values[0]);
|
2016-07-25 11:09:39 -07:00
|
|
|
// Catch contiguous intervals.
|
|
|
|
|
if (copied_values.size() ==
|
|
|
|
|
copied_values.back() - copied_values.front() + 1) {
|
|
|
|
|
return MakeNotBetweenCt(expr, copied_values.front(), copied_values.back());
|
|
|
|
|
}
|
|
|
|
|
// If the set of values in [expr.Min(), expr.Max()] that are *not* in
|
|
|
|
|
// "values" is smaller than "values", then it's more efficient to use
|
2016-07-29 14:30:14 -07:00
|
|
|
// MemberCt. Catch that case here.
|
2016-08-05 15:38:20 +02:00
|
|
|
if (emax - emin < 2 * copied_values.size()) {
|
|
|
|
|
// Convert "copied_values" to a dense boolean vector.
|
|
|
|
|
std::vector<bool> is_among_input_values(emax - emin + 1, false);
|
2021-04-01 12:13:35 +02:00
|
|
|
for (const int64_t v : copied_values)
|
|
|
|
|
is_among_input_values[v - emin] = true;
|
2016-08-05 15:38:20 +02:00
|
|
|
// Use zero valued indices for is_among_input_values to build the
|
|
|
|
|
// complement of copied_values.
|
2016-07-25 11:09:39 -07:00
|
|
|
copied_values.clear();
|
2021-04-01 12:13:35 +02:00
|
|
|
for (int64_t v_off = 0; v_off < is_among_input_values.size(); ++v_off) {
|
2020-10-22 23:36:58 +02:00
|
|
|
if (!is_among_input_values[v_off]) copied_values.push_back(v_off + emin);
|
2016-07-25 11:09:39 -07:00
|
|
|
}
|
|
|
|
|
// The empty' case (all values in range [expr.Min(), expr.Max()] are in the
|
|
|
|
|
// "values" input) was caught earlier, by the "contiguous interval" case.
|
|
|
|
|
DCHECK_GE(copied_values.size(), 1);
|
|
|
|
|
if (copied_values.size() == 1) {
|
|
|
|
|
return MakeEquality(expr, copied_values[0]);
|
|
|
|
|
}
|
|
|
|
|
return RevAlloc(new MemberCt(this, expr->Var(), copied_values));
|
|
|
|
|
}
|
|
|
|
|
// Otherwise, just use NotMemberCt. No further reduction is possible.
|
|
|
|
|
return RevAlloc(new NotMemberCt(this, expr->Var(), copied_values));
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeNotMemberCt(IntExpr* const expr,
|
|
|
|
|
const std::vector<int>& values) {
|
2016-07-25 11:09:39 -07:00
|
|
|
return MakeNotMemberCt(expr, ToInt64Vector(values));
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
// ----- IsMemberCt -----
|
|
|
|
|
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2010-09-15 12:42:33 +00:00
|
|
|
class IsMemberCt : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
IsMemberCt(Solver* const s, IntVar* const v,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& sorted_values, IntVar* const b)
|
2020-10-22 23:36:58 +02:00
|
|
|
: Constraint(s),
|
|
|
|
|
var_(v),
|
2013-07-23 18:23:51 +00:00
|
|
|
values_as_set_(sorted_values.begin(), sorted_values.end()),
|
2020-10-22 23:36:58 +02:00
|
|
|
values_(sorted_values),
|
|
|
|
|
boolvar_(b),
|
|
|
|
|
support_(0),
|
|
|
|
|
demon_(nullptr),
|
|
|
|
|
domain_(var_->MakeDomainIterator(true)),
|
2021-04-01 12:13:35 +02:00
|
|
|
neg_support_(std::numeric_limits<int64_t>::min()) {
|
2013-10-10 15:23:20 +00:00
|
|
|
DCHECK(v != nullptr);
|
|
|
|
|
DCHECK(s != nullptr);
|
|
|
|
|
DCHECK(b != nullptr);
|
2021-12-05 12:15:27 +01:00
|
|
|
while (values_as_set_.contains(neg_support_)) {
|
2013-07-29 02:49:12 +00:00
|
|
|
neg_support_++;
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
demon_ = MakeConstraintDemon0(solver(), this, &IsMemberCt::VarDomain,
|
|
|
|
|
"VarDomain");
|
2010-09-15 12:42:33 +00:00
|
|
|
if (!var_->Bound()) {
|
|
|
|
|
var_->WhenDomain(demon_);
|
|
|
|
|
}
|
|
|
|
|
if (!boolvar_->Bound()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const bdemon = MakeConstraintDemon0(
|
2013-07-23 16:09:40 +00:00
|
|
|
solver(), this, &IsMemberCt::TargetBound, "TargetBound");
|
|
|
|
|
boolvar_->WhenBound(bdemon);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2013-07-23 16:09:40 +00:00
|
|
|
boolvar_->SetRange(0, 1);
|
|
|
|
|
if (boolvar_->Bound()) {
|
|
|
|
|
TargetBound();
|
|
|
|
|
} else {
|
|
|
|
|
VarDomain();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("IsMemberCt(%s, %s, %s)", var_->DebugString(),
|
|
|
|
|
absl::StrJoin(values_, ", "),
|
|
|
|
|
boolvar_->DebugString());
|
2013-07-23 16:09:40 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2013-07-23 16:09:40 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kIsMember, this);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
|
|
|
|
var_);
|
2013-10-10 15:23:20 +00:00
|
|
|
visitor->VisitIntegerArrayArgument(ModelVisitor::kValuesArgument, values_);
|
2013-07-23 16:09:40 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
|
|
|
|
boolvar_);
|
|
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kIsMember, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2013-07-23 16:09:40 +00:00
|
|
|
void VarDomain() {
|
|
|
|
|
if (boolvar_->Bound()) {
|
|
|
|
|
TargetBound();
|
|
|
|
|
} else {
|
2013-07-23 06:10:17 +00:00
|
|
|
for (int offset = 0; offset < values_.size(); ++offset) {
|
|
|
|
|
const int candidate = (support_ + offset) % values_.size();
|
|
|
|
|
if (var_->Contains(values_[candidate])) {
|
|
|
|
|
support_ = candidate;
|
|
|
|
|
if (var_->Bound()) {
|
|
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
boolvar_->SetValue(1);
|
2013-07-24 00:28:11 +00:00
|
|
|
return;
|
2013-07-23 06:10:17 +00:00
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
// We have found a positive support. Let's check the
|
2013-07-23 15:33:27 +00:00
|
|
|
// negative support.
|
2013-07-23 18:50:52 +00:00
|
|
|
if (var_->Contains(neg_support_)) {
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
2013-07-23 15:33:27 +00:00
|
|
|
// Look for a new negative support.
|
2021-04-01 12:13:35 +02:00
|
|
|
for (const int64_t value : InitAndGetValues(domain_)) {
|
2021-12-05 12:15:27 +01:00
|
|
|
if (!values_as_set_.contains(value)) {
|
2013-07-23 18:23:51 +00:00
|
|
|
neg_support_ = value;
|
2013-07-23 15:33:27 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-07-23 06:30:04 +00:00
|
|
|
// No negative support, setting boolvar to true.
|
2013-07-23 15:33:27 +00:00
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
boolvar_->SetValue(1);
|
2013-07-23 06:30:04 +00:00
|
|
|
return;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
2013-07-23 06:10:17 +00:00
|
|
|
// No positive support, setting boolvar to false.
|
|
|
|
|
demon_->inhibit(solver());
|
|
|
|
|
boolvar_->SetValue(0);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-23 16:09:40 +00:00
|
|
|
void TargetBound() {
|
|
|
|
|
DCHECK(boolvar_->Bound());
|
|
|
|
|
if (boolvar_->Min() == 1LL) {
|
|
|
|
|
demon_->inhibit(solver());
|
2013-07-23 18:23:51 +00:00
|
|
|
var_->SetValues(values_);
|
2013-07-23 16:09:40 +00:00
|
|
|
} else {
|
|
|
|
|
demon_->inhibit(solver());
|
2013-07-23 18:23:51 +00:00
|
|
|
var_->RemoveValues(values_);
|
2013-07-23 16:09:40 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var_;
|
2021-04-01 12:13:35 +02:00
|
|
|
absl::flat_hash_set<int64_t> values_as_set_;
|
|
|
|
|
std::vector<int64_t> values_;
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const boolvar_;
|
2013-07-23 06:10:17 +00:00
|
|
|
int support_;
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* demon_;
|
|
|
|
|
IntVarIterator* const domain_;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t neg_support_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
2013-07-23 18:23:51 +00:00
|
|
|
|
2013-10-10 15:23:20 +00:00
|
|
|
template <class T>
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* BuildIsMemberCt(Solver* const solver, IntExpr* const expr,
|
|
|
|
|
const std::vector<T>& values,
|
|
|
|
|
IntVar* const boolvar) {
|
2014-11-07 14:30:53 +00:00
|
|
|
// TODO(user): optimize this by copying the code from MakeMemberCt.
|
2014-06-11 22:16:31 +00:00
|
|
|
// Simplify and filter if expr is a product.
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* sub = nullptr;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t coef = 1;
|
2014-05-25 02:13:22 +00:00
|
|
|
if (solver->IsProduct(expr, &sub, &coef) && coef != 0 && coef != 1) {
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> new_values;
|
2014-05-25 02:13:22 +00:00
|
|
|
new_values.reserve(values.size());
|
2021-04-01 12:13:35 +02:00
|
|
|
for (const int64_t value : values) {
|
2014-05-25 02:13:22 +00:00
|
|
|
if (value % coef == 0) {
|
|
|
|
|
new_values.push_back(value / coef);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return BuildIsMemberCt(solver, sub, new_values, boolvar);
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-29 02:49:12 +00:00
|
|
|
std::set<T> set_of_values(values.begin(), values.end());
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> filtered_values;
|
2014-05-15 09:19:50 +00:00
|
|
|
bool all_values = false;
|
|
|
|
|
if (expr->IsVar()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var = expr->Var();
|
2014-05-15 09:19:50 +00:00
|
|
|
for (const T value : set_of_values) {
|
|
|
|
|
if (var->Contains(value)) {
|
|
|
|
|
filtered_values.push_back(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
all_values = (filtered_values.size() == var->Size());
|
|
|
|
|
} else {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t emin = 0;
|
|
|
|
|
int64_t emax = 0;
|
2014-05-15 09:19:50 +00:00
|
|
|
expr->Range(&emin, &emax);
|
|
|
|
|
for (const T value : set_of_values) {
|
|
|
|
|
if (value >= emin && value <= emax) {
|
|
|
|
|
filtered_values.push_back(value);
|
|
|
|
|
}
|
2013-07-23 18:23:51 +00:00
|
|
|
}
|
2014-05-15 09:19:50 +00:00
|
|
|
all_values = (filtered_values.size() == emax - emin + 1);
|
2013-07-23 18:23:51 +00:00
|
|
|
}
|
|
|
|
|
if (filtered_values.empty()) {
|
|
|
|
|
return solver->MakeEquality(boolvar, Zero());
|
2014-05-15 09:19:50 +00:00
|
|
|
} else if (all_values) {
|
2013-07-23 18:23:51 +00:00
|
|
|
return solver->MakeEquality(boolvar, 1);
|
|
|
|
|
} else if (filtered_values.size() == 1) {
|
2014-05-15 09:19:50 +00:00
|
|
|
return solver->MakeIsEqualCstCt(expr, filtered_values.back(), boolvar);
|
2013-07-23 18:23:51 +00:00
|
|
|
} else if (filtered_values.back() ==
|
|
|
|
|
filtered_values.front() + filtered_values.size() - 1) {
|
|
|
|
|
// Contiguous
|
2014-05-15 09:19:50 +00:00
|
|
|
return solver->MakeIsBetweenCt(expr, filtered_values.front(),
|
2013-10-10 15:23:20 +00:00
|
|
|
filtered_values.back(), boolvar);
|
2013-07-23 18:23:51 +00:00
|
|
|
} else {
|
|
|
|
|
return solver->RevAlloc(
|
2014-05-15 09:19:50 +00:00
|
|
|
new IsMemberCt(solver, expr->Var(), filtered_values, boolvar));
|
2013-07-23 18:23:51 +00:00
|
|
|
}
|
|
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeIsMemberCt(IntExpr* const expr,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& values,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const boolvar) {
|
2014-05-15 09:19:50 +00:00
|
|
|
return BuildIsMemberCt(this, expr, values, boolvar);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeIsMemberCt(IntExpr* const expr,
|
|
|
|
|
const std::vector<int>& values,
|
|
|
|
|
IntVar* const boolvar) {
|
2014-05-15 09:19:50 +00:00
|
|
|
return BuildIsMemberCt(this, expr, values, boolvar);
|
2011-05-20 14:01:28 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* Solver::MakeIsMemberVar(IntExpr* const expr,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& values) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const b = MakeBoolVar();
|
2014-05-15 09:19:50 +00:00
|
|
|
AddConstraint(MakeIsMemberCt(expr, values, b));
|
2010-09-15 12:42:33 +00:00
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* Solver::MakeIsMemberVar(IntExpr* const expr,
|
|
|
|
|
const std::vector<int>& values) {
|
|
|
|
|
IntVar* const b = MakeBoolVar();
|
2014-05-15 09:19:50 +00:00
|
|
|
AddConstraint(MakeIsMemberCt(expr, values, b));
|
2011-05-20 14:01:28 +00:00
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-25 13:04:43 +02:00
|
|
|
namespace {
|
2016-06-02 13:19:10 +02:00
|
|
|
class SortedDisjointForbiddenIntervalsConstraint : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2016-06-02 13:19:10 +02:00
|
|
|
SortedDisjointForbiddenIntervalsConstraint(
|
2020-10-29 14:25:39 +01:00
|
|
|
Solver* const solver, IntVar* const var,
|
2016-07-13 14:51:35 -07:00
|
|
|
SortedDisjointIntervalList intervals)
|
|
|
|
|
: Constraint(solver), var_(var), intervals_(std::move(intervals)) {}
|
2015-09-25 13:04:43 +02:00
|
|
|
|
2016-06-02 13:19:10 +02:00
|
|
|
~SortedDisjointForbiddenIntervalsConstraint() override {}
|
2015-09-25 13:04:43 +02:00
|
|
|
|
|
|
|
|
void Post() override {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const demon = solver()->MakeConstraintInitialPropagateCallback(this);
|
2015-09-25 13:04:43 +02:00
|
|
|
var_->WhenRange(demon);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InitialPropagate() override {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t vmin = var_->Min();
|
|
|
|
|
const int64_t vmax = var_->Max();
|
2016-06-02 13:19:10 +02:00
|
|
|
const auto first_interval_it = intervals_.FirstIntervalGreaterOrEqual(vmin);
|
|
|
|
|
if (first_interval_it == intervals_.end()) {
|
|
|
|
|
// No interval intersects the variable's range. Nothing to do.
|
|
|
|
|
return;
|
2015-09-25 13:04:43 +02:00
|
|
|
}
|
2016-06-02 13:19:10 +02:00
|
|
|
const auto last_interval_it = intervals_.LastIntervalLessOrEqual(vmax);
|
2016-07-08 15:04:07 +02:00
|
|
|
if (last_interval_it == intervals_.end()) {
|
|
|
|
|
// No interval intersects the variable's range. Nothing to do.
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-06-02 13:19:10 +02:00
|
|
|
// TODO(user): Quick fail if first_interval_it == last_interval_it, which
|
|
|
|
|
// would imply that the interval contains the entire range of the variable?
|
|
|
|
|
if (vmin >= first_interval_it->start) {
|
|
|
|
|
// The variable's minimum is inside a forbidden interval. Move it to the
|
|
|
|
|
// interval's end.
|
|
|
|
|
var_->SetMin(CapAdd(first_interval_it->end, 1));
|
2015-09-25 13:04:43 +02:00
|
|
|
}
|
2016-06-02 13:19:10 +02:00
|
|
|
if (vmax <= last_interval_it->end) {
|
|
|
|
|
// Ditto, on the other side.
|
|
|
|
|
var_->SetMax(CapSub(last_interval_it->start, 1));
|
2015-09-25 13:04:43 +02:00
|
|
|
}
|
2016-06-02 13:19:10 +02:00
|
|
|
}
|
2015-09-25 13:04:43 +02:00
|
|
|
|
2016-06-02 13:19:10 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("ForbiddenIntervalCt(%s, %s)", var_->DebugString(),
|
|
|
|
|
intervals_.DebugString());
|
2016-06-02 13:19:10 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2016-06-02 13:19:10 +02:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kNotMember, this);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kExpressionArgument,
|
|
|
|
|
var_);
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> starts;
|
|
|
|
|
std::vector<int64_t> ends;
|
2020-10-29 14:25:39 +01:00
|
|
|
for (auto& interval : intervals_) {
|
2016-06-02 13:19:10 +02:00
|
|
|
starts.push_back(interval.start);
|
|
|
|
|
ends.push_back(interval.end);
|
2015-09-25 13:04:43 +02:00
|
|
|
}
|
2016-06-02 13:19:10 +02:00
|
|
|
visitor->VisitIntegerArrayArgument(ModelVisitor::kStartsArgument, starts);
|
|
|
|
|
visitor->VisitIntegerArrayArgument(ModelVisitor::kEndsArgument, ends);
|
|
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kNotMember, this);
|
2015-09-25 13:04:43 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var_;
|
2016-06-02 13:19:10 +02:00
|
|
|
const SortedDisjointIntervalList intervals_;
|
2015-09-25 13:04:43 +02:00
|
|
|
};
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2015-09-25 13:04:43 +02:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeNotMemberCt(IntExpr* const expr,
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> starts,
|
|
|
|
|
std::vector<int64_t> ends) {
|
2020-10-22 23:36:58 +02:00
|
|
|
return RevAlloc(new SortedDisjointForbiddenIntervalsConstraint(
|
|
|
|
|
this, expr->Var(), {starts, ends}));
|
2015-09-25 13:04:43 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeNotMemberCt(IntExpr* const expr,
|
2016-11-29 13:40:04 +01:00
|
|
|
std::vector<int> starts,
|
2016-06-02 13:19:10 +02:00
|
|
|
std::vector<int> ends) {
|
2020-10-22 23:36:58 +02:00
|
|
|
return RevAlloc(new SortedDisjointForbiddenIntervalsConstraint(
|
|
|
|
|
this, expr->Var(), {starts, ends}));
|
2016-07-13 14:51:35 -07:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeNotMemberCt(IntExpr* expr,
|
2016-07-13 14:51:35 -07:00
|
|
|
SortedDisjointIntervalList intervals) {
|
|
|
|
|
return RevAlloc(new SortedDisjointForbiddenIntervalsConstraint(
|
|
|
|
|
this, expr->Var(), std::move(intervals)));
|
2015-09-25 13:04:43 +02:00
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace operations_research
|