Files
ortools-clone/ortools/sat/cp_model.cc
2020-01-23 16:06:18 -08:00

824 lines
28 KiB
C++

// Copyright 2010-2018 Google LLC
// 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.
#include "ortools/sat/cp_model.h"
#include "absl/strings/str_format.h"
#include "ortools/base/map_util.h"
#include "ortools/sat/cp_model.pb.h"
#include "ortools/sat/cp_model_solver.h"
#include "ortools/sat/cp_model_utils.h"
#include "ortools/sat/sat_parameters.pb.h"
namespace operations_research {
namespace sat {
BoolVar::BoolVar() : cp_model_(nullptr), index_(0) {}
BoolVar::BoolVar(int index, CpModelProto* cp_model)
: cp_model_(cp_model), index_(index) {}
BoolVar BoolVar::WithName(const std::string& name) {
cp_model_->mutable_variables(index_)->set_name(name);
return *this;
}
std::string BoolVar::DebugString() const {
if (index_ < 0) {
return absl::StrFormat("Not(%s)", Not().DebugString());
} else {
std::string output;
const IntegerVariableProto& var_proto = cp_model_->variables(index_);
// Special case for constant variables without names.
if (var_proto.name().empty() && var_proto.domain_size() == 2 &&
var_proto.domain(0) == var_proto.domain(1)) {
output.append(var_proto.domain(0) == 0 ? "false" : "true");
} else {
if (var_proto.name().empty()) {
absl::StrAppendFormat(&output, "BoolVar%i(", index_);
} else {
absl::StrAppendFormat(&output, "%s(", var_proto.name());
}
if (var_proto.domain(0) == var_proto.domain(1)) {
output.append(var_proto.domain(0) == 0 ? "false)" : "true)");
} else {
absl::StrAppend(&output, var_proto.domain(0), ", ", var_proto.domain(1),
")");
}
}
return output;
}
}
BoolVar Not(BoolVar x) { return x.Not(); }
std::ostream& operator<<(std::ostream& os, const BoolVar& var) {
os << var.DebugString();
return os;
}
IntVar::IntVar() : cp_model_(nullptr), index_(0) {}
IntVar::IntVar(int index, CpModelProto* cp_model)
: cp_model_(cp_model), index_(index) {
CHECK(RefIsPositive(index));
}
IntVar IntVar::WithName(const std::string& name) {
cp_model_->mutable_variables(index_)->set_name(name);
return *this;
}
IntVar::IntVar(const BoolVar& var) {
cp_model_ = var.cp_model_;
index_ = var.index_;
}
LinearExpr IntVar::AddConstant(int64 value) const {
return LinearExpr(*this).AddConstant(value);
}
std::string IntVar::DebugString() const {
if (index_ < 0) {
return absl::StrFormat("Not(%s)",
IntVar(NegatedRef(index_), cp_model_).DebugString());
}
const IntegerVariableProto& var_proto = cp_model_->variables(index_);
// Special case for constant variables without names.
if (var_proto.name().empty() && var_proto.domain_size() == 2 &&
var_proto.domain(0) == var_proto.domain(1)) {
return absl::StrCat(var_proto.domain(0));
} else {
std::string output;
if (var_proto.name().empty()) {
absl::StrAppend(&output, "IntVar", index_, "(");
} else {
absl::StrAppend(&output, var_proto.name(), "(");
}
if (var_proto.domain_size() == 2 &&
var_proto.domain(0) == var_proto.domain(1)) {
absl::StrAppend(&output, var_proto.domain(0), ")");
} else {
// TODO(user): Use domain pretty print function.
absl::StrAppend(&output, var_proto.domain(0), ", ", var_proto.domain(1),
")");
}
return output;
}
}
std::ostream& operator<<(std::ostream& os, const IntVar& var) {
os << var.DebugString();
return os;
}
LinearExpr::LinearExpr() {}
LinearExpr::LinearExpr(BoolVar var) { AddVar(var); }
LinearExpr::LinearExpr(IntVar var) { AddVar(var); }
LinearExpr::LinearExpr(int64 constant) { constant_ = constant; }
LinearExpr LinearExpr::Sum(absl::Span<const IntVar> vars) {
LinearExpr result;
for (const IntVar& var : vars) {
result.AddVar(var);
}
return result;
}
LinearExpr LinearExpr::ScalProd(absl::Span<const IntVar> vars,
absl::Span<const int64> coeffs) {
CHECK_EQ(vars.size(), coeffs.size());
LinearExpr result;
for (int i = 0; i < vars.size(); ++i) {
result.AddTerm(vars[i], coeffs[i]);
}
return result;
}
LinearExpr LinearExpr::Term(IntVar var, int64 coefficient) {
LinearExpr result;
result.AddTerm(var, coefficient);
return result;
}
LinearExpr LinearExpr::BooleanSum(absl::Span<const BoolVar> vars) {
LinearExpr result;
for (const IntVar& var : vars) {
result.AddVar(var);
}
return result;
}
LinearExpr LinearExpr::BooleanScalProd(absl::Span<const BoolVar> vars,
absl::Span<const int64> coeffs) {
CHECK_EQ(vars.size(), coeffs.size());
LinearExpr result;
for (int i = 0; i < vars.size(); ++i) {
result.AddTerm(vars[i], coeffs[i]);
}
return result;
}
LinearExpr& LinearExpr::AddConstant(int64 value) {
constant_ += value;
return *this;
}
void LinearExpr::AddVar(IntVar var) { AddTerm(var, 1); }
void LinearExpr::AddTerm(IntVar var, int64 coeff) {
const int index = var.index_;
if (RefIsPositive(index)) {
variables_.push_back(var);
coefficients_.push_back(coeff);
} else {
variables_.push_back(IntVar(PositiveRef(var.index_), var.cp_model_));
coefficients_.push_back(-coeff);
constant_ += coeff;
}
}
Constraint::Constraint(ConstraintProto* proto) : proto_(proto) {}
Constraint Constraint::WithName(const std::string& name) {
proto_->set_name(name);
return *this;
}
const std::string& Constraint::Name() const { return proto_->name(); }
Constraint Constraint::OnlyEnforceIf(absl::Span<const BoolVar> literals) {
for (const BoolVar& var : literals) {
proto_->add_enforcement_literal(var.index_);
}
return *this;
}
Constraint Constraint::OnlyEnforceIf(BoolVar literal) {
proto_->add_enforcement_literal(literal.index_);
return *this;
}
void CircuitConstraint::AddArc(int tail, int head, BoolVar literal) {
proto_->mutable_circuit()->add_tails(tail);
proto_->mutable_circuit()->add_heads(head);
proto_->mutable_circuit()->add_literals(literal.index_);
}
void MultipleCircuitConstraint::AddArc(int tail, int head, BoolVar literal) {
proto_->mutable_routes()->add_tails(tail);
proto_->mutable_routes()->add_heads(head);
proto_->mutable_routes()->add_literals(literal.index_);
}
void TableConstraint::AddTuple(absl::Span<const int64> tuple) {
CHECK_EQ(tuple.size(), proto_->table().vars_size());
for (const int64 t : tuple) {
proto_->mutable_table()->add_values(t);
}
}
ReservoirConstraint::ReservoirConstraint(ConstraintProto* proto,
CpModelBuilder* builder)
: Constraint(proto), builder_(builder) {}
void ReservoirConstraint::AddEvent(IntVar time, int64 demand) {
proto_->mutable_reservoir()->add_times(
builder_->GetOrCreateIntegerIndex(time.index_));
proto_->mutable_reservoir()->add_demands(demand);
proto_->mutable_reservoir()->add_actives(builder_->IndexFromConstant(1));
}
void ReservoirConstraint::AddOptionalEvent(IntVar time, int64 demand,
BoolVar is_active) {
proto_->mutable_reservoir()->add_times(
builder_->GetOrCreateIntegerIndex(time.index_));
proto_->mutable_reservoir()->add_demands(demand);
proto_->mutable_reservoir()->add_actives(is_active.index_);
}
void AutomatonConstraint::AddTransition(int tail, int head,
int64 transition_label) {
proto_->mutable_automaton()->add_transition_tail(tail);
proto_->mutable_automaton()->add_transition_head(head);
proto_->mutable_automaton()->add_transition_label(transition_label);
}
void NoOverlap2DConstraint::AddRectangle(IntervalVar x_coordinate,
IntervalVar y_coordinate) {
proto_->mutable_no_overlap_2d()->add_x_intervals(x_coordinate.index_);
proto_->mutable_no_overlap_2d()->add_y_intervals(y_coordinate.index_);
}
CumulativeConstraint::CumulativeConstraint(ConstraintProto* proto,
CpModelBuilder* builder)
: Constraint(proto), builder_(builder) {}
void CumulativeConstraint::AddDemand(IntervalVar interval, IntVar demand) {
proto_->mutable_cumulative()->add_intervals(interval.index_);
proto_->mutable_cumulative()->add_demands(
builder_->GetOrCreateIntegerIndex(demand.index_));
}
IntervalVar::IntervalVar() : cp_model_(nullptr), index_() {}
IntervalVar::IntervalVar(int index, CpModelProto* cp_model)
: cp_model_(cp_model), index_(index) {}
IntervalVar IntervalVar::WithName(const std::string& name) {
cp_model_->mutable_constraints(index_)->set_name(name);
return *this;
}
IntVar IntervalVar::StartVar() const {
return IntVar(Proto().start(), cp_model_);
}
IntVar IntervalVar::SizeVar() const {
return IntVar(Proto().size(), cp_model_);
}
IntVar IntervalVar::EndVar() const { return IntVar(Proto().end(), cp_model_); }
BoolVar IntervalVar::PresenceBoolVar() const {
return BoolVar(cp_model_->constraints(index_).enforcement_literal(0),
cp_model_);
}
std::string IntervalVar::Name() const {
return cp_model_->constraints(index_).name();
}
std::string IntervalVar::DebugString() const {
CHECK_GE(index_, 0);
const ConstraintProto& ct_proto = cp_model_->constraints(index_);
std::string output;
if (ct_proto.name().empty()) {
absl::StrAppend(&output, "IntervalVar", index_, "(");
} else {
absl::StrAppend(&output, ct_proto.name(), "(");
}
absl::StrAppend(&output, StartVar().DebugString(), ", ",
SizeVar().DebugString(), ", ", EndVar().DebugString(), ", ",
PresenceBoolVar().DebugString(), ")");
return output;
}
std::ostream& operator<<(std::ostream& os, const IntervalVar& var) {
os << var.DebugString();
return os;
}
int CpModelBuilder::IndexFromConstant(int64 value) {
if (!gtl::ContainsKey(constant_to_index_map_, value)) {
const int index = cp_model_.variables_size();
IntegerVariableProto* const var_proto = cp_model_.add_variables();
var_proto->add_domain(value);
var_proto->add_domain(value);
constant_to_index_map_[value] = index;
}
return constant_to_index_map_[value];
}
int CpModelBuilder::GetOrCreateIntegerIndex(int index) {
if (index >= 0) {
return index;
}
if (!gtl::ContainsKey(bool_to_integer_index_map_, index)) {
const int ref = PositiveRef(index);
const IntegerVariableProto& old_var = cp_model_.variables(ref);
const int new_index = cp_model_.variables_size();
IntegerVariableProto* const new_var = cp_model_.add_variables();
new_var->add_domain(0);
new_var->add_domain(1);
if (!old_var.name().empty()) {
new_var->set_name(absl::StrCat("Not(", old_var.name(), ")"));
}
AddEquality(IntVar(new_index, &cp_model_), BoolVar(index, &cp_model_));
bool_to_integer_index_map_[index] = new_index;
return new_index;
}
return bool_to_integer_index_map_[index];
}
IntVar CpModelBuilder::NewIntVar(const Domain& domain) {
const int index = cp_model_.variables_size();
IntegerVariableProto* const var_proto = cp_model_.add_variables();
for (const auto& interval : domain) {
var_proto->add_domain(interval.start);
var_proto->add_domain(interval.end);
}
return IntVar(index, &cp_model_);
}
BoolVar CpModelBuilder::NewBoolVar() {
const int index = cp_model_.variables_size();
IntegerVariableProto* const var_proto = cp_model_.add_variables();
var_proto->add_domain(0);
var_proto->add_domain(1);
return BoolVar(index, &cp_model_);
}
IntVar CpModelBuilder::NewConstant(int64 value) {
return IntVar(IndexFromConstant(value), &cp_model_);
}
BoolVar CpModelBuilder::TrueVar() {
return BoolVar(IndexFromConstant(1), &cp_model_);
}
BoolVar CpModelBuilder::FalseVar() {
return BoolVar(IndexFromConstant(0), &cp_model_);
}
IntervalVar CpModelBuilder::NewIntervalVar(IntVar start, IntVar size,
IntVar end) {
return NewOptionalIntervalVar(start, size, end, TrueVar());
}
IntervalVar CpModelBuilder::NewOptionalIntervalVar(IntVar start, IntVar size,
IntVar end,
BoolVar presence) {
const int index = cp_model_.constraints_size();
ConstraintProto* const ct = cp_model_.add_constraints();
ct->add_enforcement_literal(presence.index_);
IntervalConstraintProto* const interval = ct->mutable_interval();
interval->set_start(GetOrCreateIntegerIndex(start.index_));
interval->set_size(GetOrCreateIntegerIndex(size.index_));
interval->set_end(GetOrCreateIntegerIndex(end.index_));
return IntervalVar(index, &cp_model_);
}
Constraint CpModelBuilder::AddBoolOr(absl::Span<const BoolVar> literals) {
ConstraintProto* const proto = cp_model_.add_constraints();
for (const BoolVar& lit : literals) {
proto->mutable_bool_or()->add_literals(lit.index_);
}
return Constraint(proto);
}
Constraint CpModelBuilder::AddBoolAnd(absl::Span<const BoolVar> literals) {
ConstraintProto* const proto = cp_model_.add_constraints();
for (const BoolVar& lit : literals) {
proto->mutable_bool_and()->add_literals(lit.index_);
}
return Constraint(proto);
}
Constraint CpModelBuilder::AddBoolXor(absl::Span<const BoolVar> literals) {
ConstraintProto* const proto = cp_model_.add_constraints();
for (const BoolVar& lit : literals) {
proto->mutable_bool_xor()->add_literals(lit.index_);
}
return Constraint(proto);
}
void CpModelBuilder::FillLinearTerms(const LinearExpr& left,
const LinearExpr& right,
LinearConstraintProto* proto) {
for (const IntVar& x : left.variables()) {
proto->add_vars(x.index_);
}
for (const int64 coeff : left.coefficients()) {
proto->add_coeffs(coeff);
}
for (const IntVar& x : right.variables()) {
proto->add_vars(x.index_);
}
for (const int64 coeff : right.coefficients()) {
proto->add_coeffs(-coeff);
}
}
Constraint CpModelBuilder::AddEquality(const LinearExpr& left,
const LinearExpr& right) {
ConstraintProto* const proto = cp_model_.add_constraints();
FillLinearTerms(left, right, proto->mutable_linear());
const int64 rhs = right.constant() - left.constant();
proto->mutable_linear()->add_domain(rhs);
proto->mutable_linear()->add_domain(rhs);
return Constraint(proto);
}
Constraint CpModelBuilder::AddGreaterOrEqual(const LinearExpr& left,
const LinearExpr& right) {
ConstraintProto* const proto = cp_model_.add_constraints();
FillLinearTerms(left, right, proto->mutable_linear());
const int64 rhs = right.constant() - left.constant();
proto->mutable_linear()->add_domain(rhs);
proto->mutable_linear()->add_domain(kint64max);
return Constraint(proto);
}
Constraint CpModelBuilder::AddLessOrEqual(const LinearExpr& left,
const LinearExpr& right) {
ConstraintProto* const proto = cp_model_.add_constraints();
FillLinearTerms(left, right, proto->mutable_linear());
const int64 rhs = right.constant() - left.constant();
proto->mutable_linear()->add_domain(kint64min);
proto->mutable_linear()->add_domain(rhs);
return Constraint(proto);
}
Constraint CpModelBuilder::AddGreaterThan(const LinearExpr& left,
const LinearExpr& right) {
ConstraintProto* const proto = cp_model_.add_constraints();
FillLinearTerms(left, right, proto->mutable_linear());
const int64 rhs = right.constant() - left.constant();
proto->mutable_linear()->add_domain(rhs + 1);
proto->mutable_linear()->add_domain(kint64max);
return Constraint(proto);
}
Constraint CpModelBuilder::AddLessThan(const LinearExpr& left,
const LinearExpr& right) {
ConstraintProto* const proto = cp_model_.add_constraints();
FillLinearTerms(left, right, proto->mutable_linear());
const int64 rhs = right.constant() - left.constant();
proto->mutable_linear()->add_domain(kint64min);
proto->mutable_linear()->add_domain(rhs - 1);
return Constraint(proto);
}
Constraint CpModelBuilder::AddLinearConstraint(const LinearExpr& expr,
const Domain& domain) {
ConstraintProto* const proto = cp_model_.add_constraints();
for (const IntVar& x : expr.variables()) {
proto->mutable_linear()->add_vars(x.index_);
}
for (const int64 coeff : expr.coefficients()) {
proto->mutable_linear()->add_coeffs(coeff);
}
const int64 cst = expr.constant();
for (const auto& i : domain) {
proto->mutable_linear()->add_domain(i.start - cst);
proto->mutable_linear()->add_domain(i.end - cst);
}
return Constraint(proto);
}
Constraint CpModelBuilder::AddNotEqual(const LinearExpr& left,
const LinearExpr& right) {
ConstraintProto* const proto = cp_model_.add_constraints();
FillLinearTerms(left, right, proto->mutable_linear());
const int64 rhs = right.constant() - left.constant();
proto->mutable_linear()->add_domain(kint64min);
proto->mutable_linear()->add_domain(rhs - 1);
proto->mutable_linear()->add_domain(rhs + 1);
proto->mutable_linear()->add_domain(kint64max);
return Constraint(proto);
}
Constraint CpModelBuilder::AddAllDifferent(absl::Span<const IntVar> vars) {
ConstraintProto* const proto = cp_model_.add_constraints();
for (const IntVar& var : vars) {
proto->mutable_all_diff()->add_vars(GetOrCreateIntegerIndex(var.index_));
}
return Constraint(proto);
}
Constraint CpModelBuilder::AddVariableElement(
IntVar index, absl::Span<const IntVar> variables, IntVar target) {
ConstraintProto* const proto = cp_model_.add_constraints();
proto->mutable_element()->set_index(GetOrCreateIntegerIndex(index.index_));
proto->mutable_element()->set_target(GetOrCreateIntegerIndex(target.index_));
for (const IntVar& var : variables) {
proto->mutable_element()->add_vars(GetOrCreateIntegerIndex(var.index_));
}
return Constraint(proto);
}
Constraint CpModelBuilder::AddElement(IntVar index,
absl::Span<const int64> values,
IntVar target) {
ConstraintProto* const proto = cp_model_.add_constraints();
proto->mutable_element()->set_index(GetOrCreateIntegerIndex(index.index_));
proto->mutable_element()->set_target(GetOrCreateIntegerIndex(target.index_));
for (int64 value : values) {
proto->mutable_element()->add_vars(IndexFromConstant(value));
}
return Constraint(proto);
}
CircuitConstraint CpModelBuilder::AddCircuitConstraint() {
return CircuitConstraint(cp_model_.add_constraints());
}
MultipleCircuitConstraint CpModelBuilder::AddMultipleCircuitConstraint() {
return MultipleCircuitConstraint(cp_model_.add_constraints());
}
TableConstraint CpModelBuilder::AddAllowedAssignments(
absl::Span<const IntVar> vars) {
ConstraintProto* const proto = cp_model_.add_constraints();
for (const IntVar& var : vars) {
proto->mutable_table()->add_vars(GetOrCreateIntegerIndex(var.index_));
}
return TableConstraint(proto);
}
TableConstraint CpModelBuilder::AddForbiddenAssignments(
absl::Span<const IntVar> vars) {
ConstraintProto* const proto = cp_model_.add_constraints();
for (const IntVar& var : vars) {
proto->mutable_table()->add_vars(GetOrCreateIntegerIndex(var.index_));
}
proto->mutable_table()->set_negated(true);
return TableConstraint(proto);
}
Constraint CpModelBuilder::AddInverseConstraint(
absl::Span<const IntVar> variables,
absl::Span<const IntVar> inverse_variables) {
ConstraintProto* const proto = cp_model_.add_constraints();
for (const IntVar& var : variables) {
proto->mutable_inverse()->add_f_direct(GetOrCreateIntegerIndex(var.index_));
}
for (const IntVar& var : inverse_variables) {
proto->mutable_inverse()->add_f_inverse(
GetOrCreateIntegerIndex(var.index_));
}
return Constraint(proto);
}
ReservoirConstraint CpModelBuilder::AddReservoirConstraint(int64 min_level,
int64 max_level) {
ConstraintProto* const proto = cp_model_.add_constraints();
proto->mutable_reservoir()->set_min_level(min_level);
proto->mutable_reservoir()->set_max_level(max_level);
return ReservoirConstraint(proto, this);
}
AutomatonConstraint CpModelBuilder::AddAutomaton(
absl::Span<const IntVar> transition_variables, int starting_state,
absl::Span<const int> final_states) {
ConstraintProto* const proto = cp_model_.add_constraints();
for (const IntVar& var : transition_variables) {
proto->mutable_automaton()->add_vars(GetOrCreateIntegerIndex(var.index_));
}
proto->mutable_automaton()->set_starting_state(starting_state);
for (const int final_state : final_states) {
proto->mutable_automaton()->add_final_states(final_state);
}
return AutomatonConstraint(proto);
}
Constraint CpModelBuilder::AddMinEquality(IntVar target,
absl::Span<const IntVar> vars) {
ConstraintProto* const proto = cp_model_.add_constraints();
proto->mutable_int_min()->set_target(GetOrCreateIntegerIndex(target.index_));
for (const IntVar& var : vars) {
proto->mutable_int_min()->add_vars(GetOrCreateIntegerIndex(var.index_));
}
return Constraint(proto);
}
void CpModelBuilder::LinearExprToProto(const LinearExpr& expr,
LinearExpressionProto* expr_proto) {
for (const IntVar var : expr.variables()) {
expr_proto->add_vars(GetOrCreateIntegerIndex(var.index_));
}
for (const int64 coeff : expr.coefficients()) {
expr_proto->add_coeffs(coeff);
}
expr_proto->set_offset(expr.constant());
}
Constraint CpModelBuilder::AddLinMinEquality(
const LinearExpr& target, absl::Span<const LinearExpr> exprs) {
ConstraintProto* const proto = cp_model_.add_constraints();
LinearExprToProto(target, proto->mutable_lin_min()->mutable_target());
for (const LinearExpr& expr : exprs) {
LinearExpressionProto* expr_proto = proto->mutable_lin_min()->add_exprs();
LinearExprToProto(expr, expr_proto);
}
return Constraint(proto);
}
Constraint CpModelBuilder::AddMaxEquality(IntVar target,
absl::Span<const IntVar> vars) {
ConstraintProto* const proto = cp_model_.add_constraints();
proto->mutable_int_max()->set_target(GetOrCreateIntegerIndex(target.index_));
for (const IntVar& var : vars) {
proto->mutable_int_max()->add_vars(GetOrCreateIntegerIndex(var.index_));
}
return Constraint(proto);
}
Constraint CpModelBuilder::AddLinMaxEquality(
const LinearExpr& target, absl::Span<const LinearExpr> exprs) {
ConstraintProto* const proto = cp_model_.add_constraints();
LinearExprToProto(target, proto->mutable_lin_max()->mutable_target());
for (const LinearExpr& expr : exprs) {
LinearExpressionProto* expr_proto = proto->mutable_lin_max()->add_exprs();
LinearExprToProto(expr, expr_proto);
}
return Constraint(proto);
}
Constraint CpModelBuilder::AddDivisionEquality(IntVar target, IntVar numerator,
IntVar denominator) {
ConstraintProto* const proto = cp_model_.add_constraints();
proto->mutable_int_div()->set_target(GetOrCreateIntegerIndex(target.index_));
proto->mutable_int_div()->add_vars(GetOrCreateIntegerIndex(numerator.index_));
proto->mutable_int_div()->add_vars(
GetOrCreateIntegerIndex(denominator.index_));
return Constraint(proto);
}
Constraint CpModelBuilder::AddAbsEquality(IntVar target, IntVar var) {
ConstraintProto* const proto = cp_model_.add_constraints();
proto->mutable_int_max()->set_target(GetOrCreateIntegerIndex(target.index_));
proto->mutable_int_max()->add_vars(GetOrCreateIntegerIndex(var.index_));
proto->mutable_int_max()->add_vars(
NegatedRef(GetOrCreateIntegerIndex(var.index_)));
return Constraint(proto);
}
Constraint CpModelBuilder::AddModuloEquality(IntVar target, IntVar var,
IntVar mod) {
ConstraintProto* const proto = cp_model_.add_constraints();
proto->mutable_int_mod()->set_target(GetOrCreateIntegerIndex(target.index_));
proto->mutable_int_mod()->add_vars(GetOrCreateIntegerIndex(var.index_));
proto->mutable_int_mod()->add_vars(GetOrCreateIntegerIndex(mod.index_));
return Constraint(proto);
}
Constraint CpModelBuilder::AddProductEquality(IntVar target,
absl::Span<const IntVar> vars) {
ConstraintProto* const proto = cp_model_.add_constraints();
proto->mutable_int_prod()->set_target(GetOrCreateIntegerIndex(target.index_));
for (const IntVar& var : vars) {
proto->mutable_int_prod()->add_vars(GetOrCreateIntegerIndex(var.index_));
}
return Constraint(proto);
}
Constraint CpModelBuilder::AddNoOverlap(absl::Span<const IntervalVar> vars) {
ConstraintProto* const proto = cp_model_.add_constraints();
for (const IntervalVar& var : vars) {
proto->mutable_no_overlap()->add_intervals(
GetOrCreateIntegerIndex(var.index_));
}
return Constraint(proto);
}
NoOverlap2DConstraint CpModelBuilder::AddNoOverlap2D() {
return NoOverlap2DConstraint(cp_model_.add_constraints());
}
CumulativeConstraint CpModelBuilder::AddCumulative(IntVar capacity) {
ConstraintProto* const proto = cp_model_.add_constraints();
proto->mutable_cumulative()->set_capacity(
GetOrCreateIntegerIndex(capacity.index_));
return CumulativeConstraint(proto, this);
}
void CpModelBuilder::Minimize(const LinearExpr& expr) {
cp_model_.mutable_objective()->Clear();
for (const IntVar& x : expr.variables()) {
cp_model_.mutable_objective()->add_vars(x.index_);
}
for (const int64 coeff : expr.coefficients()) {
cp_model_.mutable_objective()->add_coeffs(coeff);
}
cp_model_.mutable_objective()->set_offset(expr.constant());
}
void CpModelBuilder::Maximize(const LinearExpr& expr) {
cp_model_.mutable_objective()->Clear();
for (const IntVar& x : expr.variables()) {
cp_model_.mutable_objective()->add_vars(x.index_);
}
for (const int64 coeff : expr.coefficients()) {
cp_model_.mutable_objective()->add_coeffs(-coeff);
}
cp_model_.mutable_objective()->set_offset(-expr.constant());
cp_model_.mutable_objective()->set_scaling_factor(-1.0);
}
void CpModelBuilder::ScaleObjectiveBy(double scaling) {
CHECK(cp_model_.has_objective());
cp_model_.mutable_objective()->set_scaling_factor(
scaling * cp_model_.objective().scaling_factor());
}
void CpModelBuilder::AddDecisionStrategy(
absl::Span<const IntVar> variables,
DecisionStrategyProto::VariableSelectionStrategy var_strategy,
DecisionStrategyProto::DomainReductionStrategy domain_strategy) {
DecisionStrategyProto* const proto = cp_model_.add_search_strategy();
for (const IntVar& var : variables) {
proto->add_variables(var.index_);
}
proto->set_variable_selection_strategy(var_strategy);
proto->set_domain_reduction_strategy(domain_strategy);
}
void CpModelBuilder::AddDecisionStrategy(
absl::Span<const BoolVar> variables,
DecisionStrategyProto::VariableSelectionStrategy var_strategy,
DecisionStrategyProto::DomainReductionStrategy domain_strategy) {
DecisionStrategyProto* const proto = cp_model_.add_search_strategy();
for (const BoolVar& var : variables) {
proto->add_variables(var.index_);
}
proto->set_variable_selection_strategy(var_strategy);
proto->set_domain_reduction_strategy(domain_strategy);
}
void CpModelBuilder::AddHint(IntVar var, int64 value) {
cp_model_.mutable_solution_hint()->add_vars(
GetOrCreateIntegerIndex(var.index_));
cp_model_.mutable_solution_hint()->add_values(value);
}
int64 SolutionIntegerValue(const CpSolverResponse& r, const LinearExpr& expr) {
int64 result = expr.constant();
for (int i = 0; i < expr.variables().size(); ++i) {
result += r.solution(expr.variables()[i].index_) * expr.coefficients()[i];
}
return result;
}
int64 SolutionIntegerMin(const CpSolverResponse& r, IntVar x) {
if (r.solution_size() > 0) {
return r.solution(x.index_);
} else {
return r.solution_lower_bounds(x.index_);
}
}
int64 SolutionIntegerMax(const CpSolverResponse& r, IntVar x) {
if (r.solution_size() > 0) {
return r.solution(x.index_);
} else {
return r.solution_upper_bounds(x.index_);
}
}
bool SolutionBooleanValue(const CpSolverResponse& r, BoolVar x) {
const int ref = x.index_;
if (RefIsPositive(ref)) {
return r.solution(ref) == 1;
} else {
return r.solution(PositiveRef(ref)) == 0;
}
}
} // namespace sat
} // namespace operations_research