2016-09-12 13:51:04 +02:00
|
|
|
// Copyright 2010-2014 Google
|
|
|
|
|
// 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 "sat/integer_expr.h"
|
|
|
|
|
|
|
|
|
|
namespace operations_research {
|
|
|
|
|
namespace sat {
|
|
|
|
|
|
2016-10-05 13:53:30 +02:00
|
|
|
IntegerSumLE::IntegerSumLE(LiteralIndex reified_literal,
|
|
|
|
|
const std::vector<IntegerVariable>& vars,
|
2016-09-22 15:18:08 +02:00
|
|
|
const std::vector<IntegerValue>& coeffs,
|
|
|
|
|
IntegerValue upper, IntegerTrail* integer_trail)
|
2016-10-05 13:53:30 +02:00
|
|
|
: reified_literal_(reified_literal),
|
|
|
|
|
upper_bound_(upper),
|
2016-09-22 15:18:08 +02:00
|
|
|
vars_(vars),
|
|
|
|
|
coeffs_(coeffs),
|
|
|
|
|
integer_trail_(integer_trail) {
|
2016-09-12 13:51:04 +02:00
|
|
|
// Handle negative coefficients.
|
|
|
|
|
for (int i = 0; i < vars.size(); ++i) {
|
|
|
|
|
if (coeffs_[i] < 0) {
|
|
|
|
|
vars_[i] = NegationOf(vars_[i]);
|
|
|
|
|
coeffs_[i] = -coeffs_[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-22 15:18:08 +02:00
|
|
|
bool IntegerSumLE::Propagate(Trail* trail) {
|
|
|
|
|
CHECK(!vars_.empty()); // TODO(user): deal with this corner case.
|
2016-09-12 13:51:04 +02:00
|
|
|
|
2016-10-05 13:53:30 +02:00
|
|
|
// Reified case: If the reified literal is false, we ignore the constraint.
|
|
|
|
|
if (reified_literal_ != kNoLiteralIndex &&
|
|
|
|
|
trail->Assignment().LiteralIsFalse(Literal(reified_literal_))) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-22 15:18:08 +02:00
|
|
|
IntegerValue new_lb(0);
|
|
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2016-09-12 13:51:04 +02:00
|
|
|
new_lb += integer_trail_->LowerBound(vars_[i]) * coeffs_[i];
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-22 15:18:08 +02:00
|
|
|
// Conflict?
|
|
|
|
|
if (new_lb > upper_bound_) {
|
2016-09-12 13:51:04 +02:00
|
|
|
integer_reason_.clear();
|
|
|
|
|
for (const IntegerVariable& var : vars_) {
|
|
|
|
|
integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(var));
|
|
|
|
|
}
|
2016-10-05 13:53:30 +02:00
|
|
|
|
|
|
|
|
// Reified case: If the reified literal is unassigned, we set it to false,
|
|
|
|
|
// otherwise we have a conflict.
|
|
|
|
|
if (reified_literal_ != kNoLiteralIndex &&
|
|
|
|
|
!trail->Assignment().LiteralIsTrue(Literal(reified_literal_))) {
|
|
|
|
|
integer_trail_->EnqueueLiteral(Literal(reified_literal_).Negated(), {},
|
|
|
|
|
integer_reason_);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Conflict.
|
|
|
|
|
std::vector<Literal>* conflict = trail->MutableConflict();
|
|
|
|
|
conflict->clear();
|
|
|
|
|
if (reified_literal_ != kNoLiteralIndex) {
|
|
|
|
|
conflict->push_back(Literal(reified_literal_).Negated());
|
|
|
|
|
}
|
2016-09-22 15:18:08 +02:00
|
|
|
integer_trail_->MergeReasonInto(integer_reason_, conflict);
|
|
|
|
|
return false;
|
2016-09-12 13:51:04 +02:00
|
|
|
}
|
|
|
|
|
|
2016-10-05 13:53:30 +02:00
|
|
|
// Reified case: We can only propagate the actual constraint if the reified
|
|
|
|
|
// literal is true.
|
|
|
|
|
if (reified_literal_ != kNoLiteralIndex &&
|
|
|
|
|
!trail->Assignment().LiteralIsTrue(Literal(reified_literal_))) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-22 15:18:08 +02:00
|
|
|
// The lower bound of all the variables minus one can be used to update the
|
|
|
|
|
// upper bound of the last one.
|
2016-09-12 13:51:04 +02:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2016-09-22 15:18:08 +02:00
|
|
|
const IntegerValue new_lb_excluding_i =
|
|
|
|
|
new_lb - integer_trail_->LowerBound(vars_[i]) * coeffs_[i];
|
|
|
|
|
const IntegerValue new_term_ub = upper_bound_ - new_lb_excluding_i;
|
2016-09-12 13:51:04 +02:00
|
|
|
const IntegerValue new_ub = new_term_ub / coeffs_[i];
|
|
|
|
|
if (new_ub < integer_trail_->UpperBound(vars_[i])) {
|
2016-10-05 13:53:30 +02:00
|
|
|
literal_reason_.clear();
|
|
|
|
|
if (reified_literal_ != kNoLiteralIndex) {
|
|
|
|
|
literal_reason_.push_back(Literal(reified_literal_).Negated());
|
|
|
|
|
}
|
2016-09-12 13:51:04 +02:00
|
|
|
integer_reason_.clear();
|
|
|
|
|
for (int j = 0; j < vars_.size(); ++j) {
|
|
|
|
|
if (i == j) continue;
|
|
|
|
|
integer_reason_.push_back(
|
|
|
|
|
integer_trail_->LowerBoundAsLiteral(vars_[j]));
|
|
|
|
|
}
|
|
|
|
|
if (!integer_trail_->Enqueue(
|
|
|
|
|
IntegerLiteral::LowerOrEqual(vars_[i], new_ub), literal_reason_,
|
|
|
|
|
integer_reason_)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-22 15:18:08 +02:00
|
|
|
void IntegerSumLE::RegisterWith(GenericLiteralWatcher* watcher) {
|
2016-09-12 13:51:04 +02:00
|
|
|
const int id = watcher->Register(this);
|
|
|
|
|
for (const IntegerVariable& var : vars_) {
|
2016-09-22 15:18:08 +02:00
|
|
|
watcher->WatchLowerBound(var, id);
|
2016-09-12 13:51:04 +02:00
|
|
|
}
|
2016-10-05 13:53:30 +02:00
|
|
|
if (reified_literal_ != kNoLiteralIndex) {
|
|
|
|
|
// We only watch the true direction.
|
|
|
|
|
watcher->WatchLiteral(Literal(reified_literal_), id);
|
|
|
|
|
}
|
2016-09-12 13:51:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MinPropagator::MinPropagator(const std::vector<IntegerVariable>& vars,
|
|
|
|
|
IntegerVariable min_var,
|
|
|
|
|
IntegerTrail* integer_trail)
|
|
|
|
|
: vars_(vars), min_var_(min_var), integer_trail_(integer_trail) {}
|
|
|
|
|
|
|
|
|
|
bool MinPropagator::Propagate(Trail* trail) {
|
|
|
|
|
if (vars_.empty()) return true;
|
|
|
|
|
|
|
|
|
|
// Count the number of interval that are possible candidate for the min.
|
|
|
|
|
// Only the intervals for which lb > current_min_ub cannot.
|
|
|
|
|
const IntegerLiteral min_ub_literal =
|
|
|
|
|
integer_trail_->UpperBoundAsLiteral(min_var_);
|
|
|
|
|
const IntegerValue current_min_ub = integer_trail_->UpperBound(min_var_);
|
|
|
|
|
int num_intervals_that_can_be_min = 0;
|
|
|
|
|
int last_possible_min_interval = 0;
|
|
|
|
|
|
|
|
|
|
IntegerValue min = kMaxIntegerValue;
|
|
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
|
|
|
|
const IntegerValue lb = integer_trail_->LowerBound(vars_[i]);
|
|
|
|
|
min = std::min(min, lb);
|
|
|
|
|
if (lb <= current_min_ub) {
|
|
|
|
|
++num_intervals_that_can_be_min;
|
|
|
|
|
last_possible_min_interval = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Propagation a)
|
|
|
|
|
if (min > integer_trail_->LowerBound(min_var_)) {
|
|
|
|
|
literal_reason_.clear();
|
|
|
|
|
integer_reason_.clear();
|
|
|
|
|
for (const IntegerVariable var : vars_) {
|
|
|
|
|
integer_reason_.push_back(IntegerLiteral::GreaterOrEqual(var, min));
|
|
|
|
|
}
|
|
|
|
|
if (!integer_trail_->Enqueue(IntegerLiteral::GreaterOrEqual(min_var_, min),
|
|
|
|
|
literal_reason_, integer_reason_)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Propagation b)
|
|
|
|
|
if (num_intervals_that_can_be_min == 1) {
|
|
|
|
|
const IntegerValue ub_of_only_candidate =
|
|
|
|
|
integer_trail_->UpperBound(vars_[last_possible_min_interval]);
|
|
|
|
|
if (current_min_ub < ub_of_only_candidate) {
|
|
|
|
|
literal_reason_.clear();
|
|
|
|
|
integer_reason_.clear();
|
|
|
|
|
|
|
|
|
|
// The reason is that all the other interval start after current_min_ub.
|
|
|
|
|
// And that min_ub has its current value.
|
|
|
|
|
integer_reason_.push_back(min_ub_literal);
|
|
|
|
|
for (const IntegerVariable var : vars_) {
|
|
|
|
|
if (var == vars_[last_possible_min_interval]) continue;
|
|
|
|
|
integer_reason_.push_back(
|
|
|
|
|
IntegerLiteral::GreaterOrEqual(var, current_min_ub + 1));
|
|
|
|
|
}
|
|
|
|
|
if (!integer_trail_->Enqueue(
|
|
|
|
|
IntegerLiteral::LowerOrEqual(vars_[last_possible_min_interval],
|
|
|
|
|
current_min_ub),
|
|
|
|
|
literal_reason_, integer_reason_)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Conflict.
|
|
|
|
|
//
|
|
|
|
|
// TODO(user): Not sure this code is useful since this will be detected
|
|
|
|
|
// by the fact that the [lb, ub] of the min is empty. It depends on the
|
|
|
|
|
// propagation order though, but probably the precedences propagator would
|
|
|
|
|
// propagate before this one. So change this to a CHECK?
|
|
|
|
|
if (num_intervals_that_can_be_min == 0) {
|
|
|
|
|
integer_reason_.clear();
|
|
|
|
|
|
|
|
|
|
// Almost the same as propagation b).
|
|
|
|
|
integer_reason_.push_back(min_ub_literal);
|
|
|
|
|
for (const IntegerVariable var : vars_) {
|
|
|
|
|
integer_reason_.push_back(
|
|
|
|
|
IntegerLiteral::GreaterOrEqual(var, current_min_ub + 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<Literal>* conflict = trail->MutableConflict();
|
|
|
|
|
integer_trail_->MergeReasonInto(integer_reason_, conflict);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MinPropagator::RegisterWith(GenericLiteralWatcher* watcher) {
|
|
|
|
|
const int id = watcher->Register(this);
|
|
|
|
|
for (const IntegerVariable& var : vars_) {
|
|
|
|
|
watcher->WatchLowerBound(var, id);
|
|
|
|
|
}
|
|
|
|
|
watcher->WatchUpperBound(min_var_, id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IsOneOfPropagator::IsOneOfPropagator(IntegerVariable var,
|
|
|
|
|
const std::vector<Literal>& selectors,
|
|
|
|
|
const std::vector<IntegerValue>& values,
|
|
|
|
|
IntegerTrail* integer_trail)
|
|
|
|
|
: var_(var),
|
|
|
|
|
selectors_(selectors),
|
|
|
|
|
values_(values),
|
|
|
|
|
integer_trail_(integer_trail) {}
|
|
|
|
|
|
|
|
|
|
bool IsOneOfPropagator::Propagate(Trail* trail) {
|
|
|
|
|
const IntegerValue current_min = integer_trail_->LowerBound(var_);
|
|
|
|
|
const IntegerValue current_max = integer_trail_->UpperBound(var_);
|
|
|
|
|
IntegerValue min = kMaxIntegerValue;
|
|
|
|
|
IntegerValue max = kMinIntegerValue;
|
|
|
|
|
literal_reason_.clear();
|
|
|
|
|
for (int i = 0; i < selectors_.size(); ++i) {
|
|
|
|
|
if (trail->Assignment().LiteralIsFalse(selectors_[i])) {
|
|
|
|
|
literal_reason_.push_back(selectors_[i]);
|
|
|
|
|
} else {
|
|
|
|
|
min = std::min(min, values_[i]);
|
|
|
|
|
max = std::max(max, values_[i]);
|
|
|
|
|
|
|
|
|
|
if (!trail->Assignment().LiteralIsTrue(selectors_[i])) {
|
|
|
|
|
// Propagate selector to false?
|
|
|
|
|
std::vector<Literal>* literal_reason;
|
|
|
|
|
std::vector<IntegerLiteral>* integer_reason;
|
|
|
|
|
if (current_min > values_[i]) {
|
2016-09-22 13:55:16 +02:00
|
|
|
integer_trail_->EnqueueLiteral(selectors_[i].Negated(),
|
|
|
|
|
&literal_reason, &integer_reason);
|
2016-09-12 13:51:04 +02:00
|
|
|
integer_reason->push_back(integer_trail_->LowerBoundAsLiteral(var_));
|
|
|
|
|
} else if (current_max < values_[i]) {
|
2016-09-22 13:55:16 +02:00
|
|
|
integer_trail_->EnqueueLiteral(selectors_[i].Negated(),
|
|
|
|
|
&literal_reason, &integer_reason);
|
2016-09-12 13:51:04 +02:00
|
|
|
integer_reason->push_back(integer_trail_->UpperBoundAsLiteral(var_));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Propagate new min/max.
|
|
|
|
|
if (min > current_min) {
|
|
|
|
|
if (!integer_trail_->Enqueue(IntegerLiteral::GreaterOrEqual(var_, min),
|
|
|
|
|
literal_reason_, integer_reason_)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (max < current_max) {
|
|
|
|
|
if (!integer_trail_->Enqueue(IntegerLiteral::LowerOrEqual(var_, max),
|
|
|
|
|
literal_reason_, integer_reason_)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IsOneOfPropagator::RegisterWith(GenericLiteralWatcher* watcher) {
|
|
|
|
|
const int id = watcher->Register(this);
|
|
|
|
|
for (const Literal& lit : selectors_) {
|
|
|
|
|
watcher->WatchLiteral(lit.Negated(), id);
|
|
|
|
|
}
|
|
|
|
|
watcher->WatchIntegerVariable(var_, id);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-05 13:53:30 +02:00
|
|
|
ProductPropagator::ProductPropagator(IntegerVariable a, IntegerVariable b,
|
|
|
|
|
IntegerVariable p,
|
|
|
|
|
IntegerTrail* integer_trail)
|
|
|
|
|
: a_(a), b_(b), p_(p), integer_trail_(integer_trail) {}
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
// The maximum value of x such that x * b <= p with b > 0 and p >= 0;
|
|
|
|
|
IntegerValue MaxValue(IntegerValue b, IntegerValue p) {
|
|
|
|
|
CHECK_GT(b, 0);
|
|
|
|
|
CHECK_GE(p, 0);
|
|
|
|
|
return p / b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The minimum value of x such that x * b >= p with b > 0 and p >= 0;
|
|
|
|
|
IntegerValue MinValue(IntegerValue b, IntegerValue p) {
|
|
|
|
|
CHECK_GT(b, 0);
|
|
|
|
|
CHECK_GE(p, 0);
|
|
|
|
|
return (p + b - 1) / b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
bool ProductPropagator::Propagate(Trail* trail) {
|
|
|
|
|
// Copy because we will swap them.
|
|
|
|
|
IntegerVariable a = a_;
|
|
|
|
|
IntegerVariable b = b_;
|
|
|
|
|
IntegerValue min_a = integer_trail_->LowerBound(a);
|
|
|
|
|
IntegerValue max_a = integer_trail_->UpperBound(a);
|
|
|
|
|
IntegerValue min_b = integer_trail_->LowerBound(b);
|
|
|
|
|
IntegerValue max_b = integer_trail_->UpperBound(b);
|
|
|
|
|
IntegerValue min_p = integer_trail_->LowerBound(p_);
|
|
|
|
|
IntegerValue max_p = integer_trail_->UpperBound(p_);
|
|
|
|
|
|
|
|
|
|
// TODO(user): support these cases.
|
|
|
|
|
CHECK_GE(min_a, 0);
|
|
|
|
|
CHECK_GE(min_b, 0);
|
|
|
|
|
|
|
|
|
|
const IntegerValue zero(0); // For convenience.
|
|
|
|
|
bool may_propagate = true;
|
|
|
|
|
while (may_propagate) {
|
|
|
|
|
may_propagate = false;
|
|
|
|
|
if (max_a * max_b < max_p) {
|
|
|
|
|
may_propagate = true;
|
|
|
|
|
max_p = max_a * max_b;
|
|
|
|
|
if (!integer_trail_->Enqueue(IntegerLiteral::LowerOrEqual(p_, max_p), {},
|
|
|
|
|
{integer_trail_->UpperBoundAsLiteral(a),
|
|
|
|
|
integer_trail_->UpperBoundAsLiteral(b),
|
|
|
|
|
IntegerLiteral::GreaterOrEqual(a, zero),
|
|
|
|
|
IntegerLiteral::GreaterOrEqual(b, zero)})) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (min_a * min_b > min_p) {
|
|
|
|
|
may_propagate = true;
|
|
|
|
|
min_p = min_a * min_b;
|
|
|
|
|
if (!integer_trail_->Enqueue(IntegerLiteral::GreaterOrEqual(p_, min_p),
|
|
|
|
|
{},
|
|
|
|
|
{integer_trail_->LowerBoundAsLiteral(a),
|
|
|
|
|
integer_trail_->LowerBoundAsLiteral(b)})) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This helps to check the validity of the test below.
|
|
|
|
|
CHECK_GE(min_p, 0);
|
|
|
|
|
CHECK_GE(max_p, min_p);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
|
|
|
if (max_a * min_b > max_p) {
|
|
|
|
|
may_propagate = true;
|
|
|
|
|
max_a = MaxValue(min_b, max_p);
|
|
|
|
|
if (!integer_trail_->Enqueue(
|
|
|
|
|
IntegerLiteral::LowerOrEqual(a, max_a), {},
|
|
|
|
|
{integer_trail_->LowerBoundAsLiteral(b),
|
|
|
|
|
integer_trail_->UpperBoundAsLiteral(p_)})) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else if (max_a * min_b < min_p) {
|
|
|
|
|
may_propagate = true;
|
|
|
|
|
min_b = MinValue(max_a, min_p);
|
|
|
|
|
if (!integer_trail_->Enqueue(
|
|
|
|
|
IntegerLiteral::GreaterOrEqual(b, min_b), {},
|
|
|
|
|
{integer_trail_->UpperBoundAsLiteral(a),
|
|
|
|
|
IntegerLiteral::GreaterOrEqual(b, zero),
|
|
|
|
|
integer_trail_->LowerBoundAsLiteral(p_)})) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Same thing with a and b swapped.
|
|
|
|
|
std::swap(a, b);
|
|
|
|
|
std::swap(min_a, min_b);
|
|
|
|
|
std::swap(max_a, max_b);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ProductPropagator::RegisterWith(GenericLiteralWatcher* watcher) {
|
|
|
|
|
const int id = watcher->Register(this);
|
|
|
|
|
watcher->WatchIntegerVariable(a_, id);
|
|
|
|
|
watcher->WatchIntegerVariable(b_, id);
|
|
|
|
|
watcher->WatchIntegerVariable(p_, id);
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-12 13:51:04 +02:00
|
|
|
} // namespace sat
|
|
|
|
|
} // namespace operations_research
|