Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
cp_constraints.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <utility>
18#include <vector>
19
20#include "absl/log/check.h"
21#include "absl/types/span.h"
23#include "ortools/sat/integer.h"
25#include "ortools/sat/model.h"
28
29namespace operations_research {
30namespace sat {
31
33 absl::Span<const Literal> enforcement_literals,
34 const std::vector<Literal>& literals, bool value, Model* model)
35 : literals_(literals),
36 value_(value),
37 trail_(*model->GetOrCreate<Trail>()),
38 integer_trail_(*model->GetOrCreate<IntegerTrail>()),
39 enforcement_helper_(*model->GetOrCreate<EnforcementHelper>()) {
41 enforcement_id_ = enforcement_helper_.Register(enforcement_literals, watcher,
42 RegisterWith(watcher));
43}
44
46 const EnforcementStatus status = enforcement_helper_.Status(enforcement_id_);
47 if (status == EnforcementStatus::IS_FALSE ||
49 return true;
50 }
51
52 bool sum = false;
53 int unassigned_index = -1;
54 for (int i = 0; i < literals_.size(); ++i) {
55 const Literal l = literals_[i];
56 if (trail_.Assignment().LiteralIsFalse(l)) {
57 sum ^= false;
58 } else if (trail_.Assignment().LiteralIsTrue(l)) {
59 sum ^= true;
60 } else {
61 // If we have more than one unassigned literal, we can't deduce anything.
62 if (unassigned_index != -1) return true;
63 unassigned_index = i;
64 }
65 }
66
67 auto fill_literal_reason = [&]() {
68 literal_reason_.clear();
69 for (int i = 0; i < literals_.size(); ++i) {
70 if (i == unassigned_index) continue;
71 const Literal l = literals_[i];
72 literal_reason_.push_back(
73 trail_.Assignment().LiteralIsFalse(l) ? l : l.Negated());
74 }
75 };
76
77 // Propagates?
78 if (status == EnforcementStatus::IS_ENFORCED && unassigned_index != -1) {
79 fill_literal_reason();
80 const Literal u = literals_[unassigned_index];
81 return enforcement_helper_.EnqueueLiteral(
82 enforcement_id_, sum == value_ ? u.Negated() : u, literal_reason_,
83 /*integer_reason=*/{});
84 }
85
86 // Ok.
87 if (sum == value_) return true;
88
89 // Conflict.
90 if (status == EnforcementStatus::IS_ENFORCED) {
91 fill_literal_reason();
92 return enforcement_helper_.ReportConflict(enforcement_id_, literal_reason_,
93 /*integer_reason=*/{});
94 } else if (unassigned_index == -1) {
95 fill_literal_reason();
96 return enforcement_helper_.PropagateWhenFalse(
97 enforcement_id_, literal_reason_, /*integer_reason=*/{});
98 } else {
99 return true;
100 }
101}
102
103int BooleanXorPropagator::RegisterWith(GenericLiteralWatcher* watcher) {
104 const int id = watcher->Register(this);
105 for (const Literal& l : literals_) {
106 watcher->WatchLiteral(l, id);
107 watcher->WatchLiteral(l.Negated(), id);
108 }
109 return id;
110}
111
113 IntegerVariable target_var, const absl::Span<const AffineExpression> exprs,
114 const absl::Span<const Literal> selectors,
115 const absl::Span<const Literal> enforcements, Model* model)
116 : target_var_(target_var),
117 enforcements_(enforcements.begin(), enforcements.end()),
118 selectors_(selectors.begin(), selectors.end()),
119 exprs_(exprs.begin(), exprs.end()),
120 trail_(model->GetOrCreate<Trail>()),
121 integer_trail_(model->GetOrCreate<IntegerTrail>()) {}
122
124 IntegerLiteral to_explain,
125 IntegerReason* reason) {
126 const int first_non_false = id;
127
128 // Note that even if we propagated more, we only need the reason for
129 // to_explain.bound, this is easy for that propagator.
130 CHECK_EQ(to_explain.var, target_var_);
131 const IntegerValue target_min = to_explain.bound;
132
133 reason->boolean_literals_at_true = enforcements_;
134
135 auto& literals_at_false = integer_trail_->ClearedMutableTmpLiterals();
136 for (int i = 0; i < first_non_false; ++i) {
137 // If the level zero bounds is good enough, no reason needed.
138 //
139 // TODO(user): We could also skip this if we already have the reason for
140 // the expression being high enough in the current conflict.
141 if (integer_trail_->LevelZeroLowerBound(exprs_[i]) >= target_min) {
142 continue;
143 }
144
145 literals_at_false.push_back(selectors_[i]);
146 }
147 reason->boolean_literals_at_false = literals_at_false;
148
149 // Add all greater than target_min reason.
150 auto& integer_literals = integer_trail_->ClearedMutableTmpIntegerLiterals();
151 for (const AffineExpression& expr :
152 absl::MakeSpan(exprs_).subspan(first_non_false)) {
153 if (expr.IsConstant()) {
154 DCHECK_GE(expr.constant, target_min);
155 continue;
156 }
157 DCHECK_NE(expr.var, kNoIntegerVariable);
158 const IntegerLiteral to_explain = expr.GreaterOrEqual(target_min);
159 if (integer_trail_->IsTrueAtLevelZero(to_explain)) continue;
160 integer_literals.push_back(to_explain);
161 }
162 reason->integer_literals = integer_literals;
163}
164
166 // TODO(user): In case of a conflict, we could push one of them to false if
167 // it is the only one.
168 for (const Literal l : enforcements_) {
169 if (!trail_->Assignment().LiteralIsTrue(l)) return true;
170 }
171
172 // Compute the min of the lower-bound for the still possible variables.
173 // TODO(user): This could be optimized by keeping more info from the last
174 // Propagate() calls.
175 IntegerValue target_min = kMaxIntegerValue;
176 const IntegerValue current_min = integer_trail_->LowerBound(target_var_);
177 const AssignmentView assignment(trail_->Assignment());
178
179 int first_non_false = 0;
180 const int size = exprs_.size();
181 Literal* const selectors = selectors_.data();
182 AffineExpression* const exprs = exprs_.data();
183 for (int i = 0; i < size; ++i) {
184 if (assignment.LiteralIsTrue(selectors[i])) return true;
185
186 // The permutation is needed to have proper lazy reason.
187 if (assignment.LiteralIsFalse(selectors[i])) {
188 if (i != first_non_false) {
189 std::swap(selectors[i], selectors[first_non_false]);
190 std::swap(exprs[i], exprs[first_non_false]);
191 }
192 ++first_non_false;
193 continue;
194 }
195
196 const IntegerValue min = integer_trail_->LowerBound(exprs[i]);
197 if (min < target_min) {
198 target_min = min;
199
200 // Abort if we can't get a better bound.
201 if (target_min <= current_min) return true;
202 }
203 }
204
205 if (target_min == kMaxIntegerValue) {
206 // All false, conflit.
207 *(trail_->MutableConflict()) = selectors_;
208 return false;
209 }
210
211 // Note that we use id/propagation_slack for other purpose.
212 return integer_trail_->EnqueueWithLazyReason(
213 IntegerLiteral::GreaterOrEqual(target_var_, target_min),
214 /*id=*/first_non_false, /*propagation_slack=*/target_min, this);
215}
216
218 GenericLiteralWatcher* watcher) {
219 const int id = watcher->Register(this);
220 for (const Literal l : selectors_) watcher->WatchLiteral(l.Negated(), id);
221 for (const Literal l : enforcements_) watcher->WatchLiteral(l, id);
222 for (const AffineExpression e : exprs_) {
223 if (!e.IsConstant()) {
224 watcher->WatchLowerBound(e, id);
225 }
226 }
227}
228
229} // namespace sat
230} // namespace operations_research
bool LiteralIsFalse(Literal literal) const
Definition sat_base.h:249
bool LiteralIsTrue(Literal literal) const
Definition sat_base.h:253
BooleanXorPropagator(absl::Span< const Literal > enforcement_literals, const std::vector< Literal > &literals, bool value, Model *model)
void WatchLiteral(Literal l, int id, int watch_index=-1)
Definition integer.h:1708
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
Definition integer.h:1716
int Register(PropagatorInterface *propagator)
Definition integer.cc:2674
GreaterThanAtLeastOneOfPropagator(IntegerVariable target_var, absl::Span< const AffineExpression > exprs, absl::Span< const Literal > selectors, absl::Span< const Literal > enforcements, Model *model)
void Explain(int id, IntegerLiteral to_explain, IntegerReason *reason) final
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
const IntegerVariable kNoIntegerVariable(-1)
OR-Tools root namespace.
ClosedInterval::Iterator end(ClosedInterval interval)
ClosedInterval::Iterator begin(ClosedInterval interval)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
absl::Span< const Literal > boolean_literals_at_false
Definition integer.h:441
absl::Span< const Literal > boolean_literals_at_true
Definition integer.h:440
absl::Span< const IntegerLiteral > integer_literals
Definition integer.h:442