Files
ortools-clone/ortools/sat/cp_constraints.cc

231 lines
8.0 KiB
C++
Raw Permalink Normal View History

2025-01-10 11:35:44 +01:00
// Copyright 2010-2025 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_constraints.h"
2017-07-27 11:28:55 -07:00
#include <algorithm>
2025-08-06 10:54:53 +02:00
#include <utility>
#include <vector>
2025-11-05 13:55:12 +01:00
#include "absl/log/check.h"
#include "absl/types/span.h"
#include "ortools/sat/enforcement.h"
#include "ortools/sat/integer.h"
#include "ortools/sat/integer_base.h"
#include "ortools/sat/model.h"
#include "ortools/sat/sat_base.h"
#include "ortools/util/strong_integers.h"
namespace operations_research {
namespace sat {
2025-08-06 10:54:53 +02:00
BooleanXorPropagator::BooleanXorPropagator(
absl::Span<const Literal> enforcement_literals,
const std::vector<Literal>& literals, bool value, Model* model)
: literals_(literals),
value_(value),
trail_(*model->GetOrCreate<Trail>()),
integer_trail_(*model->GetOrCreate<IntegerTrail>()),
enforcement_helper_(*model->GetOrCreate<EnforcementHelper>()) {
2025-08-06 10:54:53 +02:00
GenericLiteralWatcher* watcher = model->GetOrCreate<GenericLiteralWatcher>();
enforcement_id_ = enforcement_helper_.Register(enforcement_literals, watcher,
RegisterWith(watcher));
2025-08-06 10:54:53 +02:00
}
bool BooleanXorPropagator::Propagate() {
const EnforcementStatus status = enforcement_helper_.Status(enforcement_id_);
2025-08-06 10:54:53 +02:00
if (status == EnforcementStatus::IS_FALSE ||
status == EnforcementStatus::CANNOT_PROPAGATE) {
return true;
}
2016-10-12 12:07:20 -07:00
bool sum = false;
int unassigned_index = -1;
for (int i = 0; i < literals_.size(); ++i) {
const Literal l = literals_[i];
if (trail_.Assignment().LiteralIsFalse(l)) {
2016-10-12 12:07:20 -07:00
sum ^= false;
} else if (trail_.Assignment().LiteralIsTrue(l)) {
2016-10-12 12:07:20 -07:00
sum ^= true;
} else {
// If we have more than one unassigned literal, we can't deduce anything.
2020-10-22 23:36:58 +02:00
if (unassigned_index != -1) return true;
2016-10-12 12:07:20 -07:00
unassigned_index = i;
}
}
auto fill_literal_reason = [&]() {
2016-10-20 22:05:03 +02:00
literal_reason_.clear();
2016-10-12 12:07:20 -07:00
for (int i = 0; i < literals_.size(); ++i) {
2020-10-22 23:36:58 +02:00
if (i == unassigned_index) continue;
2016-10-12 12:07:20 -07:00
const Literal l = literals_[i];
2016-10-20 22:05:03 +02:00
literal_reason_.push_back(
trail_.Assignment().LiteralIsFalse(l) ? l : l.Negated());
2016-10-12 12:07:20 -07:00
}
};
// Propagates?
if (status == EnforcementStatus::IS_ENFORCED && unassigned_index != -1) {
fill_literal_reason();
2016-10-20 22:05:03 +02:00
const Literal u = literals_[unassigned_index];
2025-11-05 13:55:12 +01:00
return enforcement_helper_.EnqueueLiteral(
enforcement_id_, sum == value_ ? u.Negated() : u, literal_reason_,
/*integer_reason=*/{});
2025-08-06 10:54:53 +02:00
}
2016-10-12 12:07:20 -07:00
// Ok.
2020-10-22 23:36:58 +02:00
if (sum == value_) return true;
2016-10-12 12:07:20 -07:00
// Conflict.
if (status == EnforcementStatus::IS_ENFORCED) {
fill_literal_reason();
return enforcement_helper_.ReportConflict(enforcement_id_, literal_reason_,
/*integer_reason=*/{});
} else if (unassigned_index == -1) {
fill_literal_reason();
return enforcement_helper_.PropagateWhenFalse(
enforcement_id_, literal_reason_, /*integer_reason=*/{});
} else {
return true;
2016-10-12 12:07:20 -07:00
}
}
2025-08-06 10:54:53 +02:00
int BooleanXorPropagator::RegisterWith(GenericLiteralWatcher* watcher) {
2016-10-12 12:07:20 -07:00
const int id = watcher->Register(this);
for (const Literal& l : literals_) {
2016-10-12 12:07:20 -07:00
watcher->WatchLiteral(l, id);
watcher->WatchLiteral(l.Negated(), id);
}
2025-08-06 10:54:53 +02:00
return id;
2016-10-12 12:07:20 -07:00
}
GreaterThanAtLeastOneOfPropagator::GreaterThanAtLeastOneOfPropagator(
IntegerVariable target_var, const absl::Span<const AffineExpression> exprs,
2019-04-03 18:10:56 +02:00
const absl::Span<const Literal> selectors,
const absl::Span<const Literal> enforcements, Model* model)
2020-10-22 23:36:58 +02:00
: target_var_(target_var),
2019-04-03 18:10:56 +02:00
enforcements_(enforcements.begin(), enforcements.end()),
selectors_(selectors.begin(), selectors.end()),
exprs_(exprs.begin(), exprs.end()),
trail_(model->GetOrCreate<Trail>()),
integer_trail_(model->GetOrCreate<IntegerTrail>()) {}
2025-11-05 13:55:12 +01:00
void GreaterThanAtLeastOneOfPropagator::Explain(int id,
IntegerLiteral to_explain,
IntegerReason* reason) {
const int first_non_false = id;
2025-11-05 13:55:12 +01:00
// Note that even if we propagated more, we only need the reason for
// to_explain.bound, this is easy for that propagator.
CHECK_EQ(to_explain.var, target_var_);
const IntegerValue target_min = to_explain.bound;
reason->boolean_literals_at_true = enforcements_;
auto& literals_at_false = integer_trail_->ClearedMutableTmpLiterals();
for (int i = 0; i < first_non_false; ++i) {
// If the level zero bounds is good enough, no reason needed.
//
// TODO(user): We could also skip this if we already have the reason for
// the expression being high enough in the current conflict.
if (integer_trail_->LevelZeroLowerBound(exprs_[i]) >= target_min) {
continue;
}
2025-11-05 13:55:12 +01:00
literals_at_false.push_back(selectors_[i]);
}
reason->boolean_literals_at_false = literals_at_false;
// Add all greater than target_min reason.
auto& integer_literals = integer_trail_->ClearedMutableTmpIntegerLiterals();
for (const AffineExpression& expr :
absl::MakeSpan(exprs_).subspan(first_non_false)) {
if (expr.IsConstant()) {
DCHECK_GE(expr.constant, target_min);
continue;
}
DCHECK_NE(expr.var, kNoIntegerVariable);
const IntegerLiteral to_explain = expr.GreaterOrEqual(target_min);
if (integer_trail_->IsTrueAtLevelZero(to_explain)) continue;
integer_literals.push_back(to_explain);
}
2025-11-05 13:55:12 +01:00
reason->integer_literals = integer_literals;
}
bool GreaterThanAtLeastOneOfPropagator::Propagate() {
2019-04-03 18:10:56 +02:00
// TODO(user): In case of a conflict, we could push one of them to false if
// it is the only one.
for (const Literal l : enforcements_) {
2020-10-22 23:36:58 +02:00
if (!trail_->Assignment().LiteralIsTrue(l)) return true;
2019-04-03 18:10:56 +02:00
}
// Compute the min of the lower-bound for the still possible variables.
// TODO(user): This could be optimized by keeping more info from the last
// Propagate() calls.
IntegerValue target_min = kMaxIntegerValue;
const IntegerValue current_min = integer_trail_->LowerBound(target_var_);
const AssignmentView assignment(trail_->Assignment());
int first_non_false = 0;
const int size = exprs_.size();
2025-01-31 17:09:37 +01:00
Literal* const selectors = selectors_.data();
AffineExpression* const exprs = exprs_.data();
for (int i = 0; i < size; ++i) {
2025-01-31 17:09:37 +01:00
if (assignment.LiteralIsTrue(selectors[i])) return true;
// The permutation is needed to have proper lazy reason.
2025-01-31 17:09:37 +01:00
if (assignment.LiteralIsFalse(selectors[i])) {
if (i != first_non_false) {
2025-01-31 17:09:37 +01:00
std::swap(selectors[i], selectors[first_non_false]);
std::swap(exprs[i], exprs[first_non_false]);
}
++first_non_false;
continue;
}
2025-01-31 17:09:37 +01:00
const IntegerValue min = integer_trail_->LowerBound(exprs[i]);
if (min < target_min) {
target_min = min;
// Abort if we can't get a better bound.
if (target_min <= current_min) return true;
}
}
if (target_min == kMaxIntegerValue) {
// All false, conflit.
*(trail_->MutableConflict()) = selectors_;
return false;
}
// Note that we use id/propagation_slack for other purpose.
return integer_trail_->EnqueueWithLazyReason(
IntegerLiteral::GreaterOrEqual(target_var_, target_min),
/*id=*/first_non_false, /*propagation_slack=*/target_min, this);
}
void GreaterThanAtLeastOneOfPropagator::RegisterWith(
GenericLiteralWatcher* watcher) {
const int id = watcher->Register(this);
2020-10-22 23:36:58 +02:00
for (const Literal l : selectors_) watcher->WatchLiteral(l.Negated(), id);
for (const Literal l : enforcements_) watcher->WatchLiteral(l, id);
2024-03-21 11:34:50 +01:00
for (const AffineExpression e : exprs_) {
if (!e.IsConstant()) {
watcher->WatchLowerBound(e, id);
}
}
}
2020-10-22 23:36:58 +02:00
} // namespace sat
} // namespace operations_research