Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
integer_resolution.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 <cstdint>
18#include <optional>
19#include <string>
20#include <utility>
21#include <vector>
22
23#include "absl/log/check.h"
24#include "absl/log/log.h"
25#include "absl/strings/str_cat.h"
26#include "absl/types/span.h"
27#include "ortools/sat/clause.h"
28#include "ortools/sat/integer.h"
30#include "ortools/sat/model.h"
34
36
38 : trail_(model->GetOrCreate<Trail>()),
39 integer_trail_(model->GetOrCreate<IntegerTrail>()),
40 integer_encoder_(model->GetOrCreate<IntegerEncoder>()),
41 sat_solver_(model->GetOrCreate<SatSolver>()),
42 shared_stats_(model->GetOrCreate<SharedStatistics>()),
43 clauses_propagator_(model->GetOrCreate<ClauseManager>()),
44 implications_(model->GetOrCreate<BinaryImplicationGraph>()),
45 params_(*model->GetOrCreate<SatParameters>()) {
46 trail_->SetConflictResolutionFunction(
47 [this](std::vector<Literal>* conflict,
48 std::vector<Literal>* reason_used_to_infer_the_conflict,
49 std::vector<SatClause*>* subsumed_clauses) {
50 ComputeFirstUIPConflict(conflict, reason_used_to_infer_the_conflict,
51 subsumed_clauses);
52 });
53 integer_trail_->UseNewConflictResolution();
54}
55
57 if (!VLOG_IS_ON(1)) return;
58 std::vector<std::pair<std::string, int64_t>> stats;
59 stats.push_back(
60 {"IntegerConflictResolution/num_expansions", num_expansions_});
61 stats.push_back({"IntegerConflictResolution/num_conflicts_at_wrong_level",
62 num_conflicts_at_wrong_level_});
63 stats.push_back({"IntegerConflictResolution/num_subsumed", num_subsumed_});
64 stats.push_back({"IntegerConflictResolution/num_conflict_literals",
65 num_conflict_literals_});
66 stats.push_back({"IntegerConflictResolution/num_associated",
67 num_associated_integer_for_literals_in_conflict_});
68
69 stats.push_back({"IntegerConflictResolution/num_asso_lit_uses",
70 num_associated_literal_use_});
71 stats.push_back({"IntegerConflictResolution/num_asso_lit_fails",
72 num_associated_literal_fail_});
73 stats.push_back({"IntegerConflictResolution/num_possibly_non_optimal_reason",
74 num_possibly_non_optimal_reason_});
75 stats.push_back(
76 {"IntegerConflictResolution/num_slack_usage", num_slack_usage_});
77 stats.push_back(
78 {"IntegerConflictResolution/num_slack_relax", num_slack_relax_});
79 stats.push_back(
80 {"IntegerConflictResolution/num_holes_relax", num_holes_relax_});
81 stats.push_back(
82 {"IntegerConflictResolution/num_new_1uip_bools", num_created_1uip_bool_});
83 stats.push_back({"IntegerConflictResolution/num_binary_minimizations",
84 num_binary_minimization_});
85
86 if (comparison_old_sum_of_literals_ > 0) {
87 stats.push_back({"Comparison/num_win", comparison_num_win_});
88 stats.push_back({"Comparison/num_same", comparison_num_same_});
89 stats.push_back({"Comparison/num_loose", comparison_num_loose_});
90 stats.push_back(
91 {"Comparison/old_sum_of_literals", comparison_old_sum_of_literals_});
92 stats.push_back(
93 {"Comparison/old_num_subsumed", comparison_old_num_subsumed_});
94 }
95
96 shared_stats_->AddStats(stats);
97}
98
99absl::Span<const IntegerLiteral>
100IntegerConflictResolution::IndexToIntegerLiterals(GlobalTrailIndex index) {
101 if (index.IsInteger()) {
102 tmp_integer_literals_.clear();
103 tmp_integer_literals_.push_back(
104 integer_trail_->IntegerLiteralAtIndex(index.integer_index));
105 return tmp_integer_literals_;
106 } else if (index.bool_index < trail_->Index()) {
107 const Literal literal = (*trail_)[index.bool_index];
108 return integer_encoder_->GetIntegerLiterals(literal);
109 }
110 return {};
111}
112
113IntegerValue IntegerConflictResolution::RelaxBoundIfHoles(IntegerVariable var,
114 IntegerValue value) {
115 if (!integer_encoder_->VariableDomainHasHoles(var)) return value;
116 const auto [cano_lit, negated_lit] = integer_encoder_->Canonicalize(
118 const IntegerValue relaxed = negated_lit.Negated().bound;
119 if (relaxed != value) {
120 CHECK_LE(relaxed, value);
121 value = relaxed;
122 ++num_holes_relax_;
123 }
124 return value;
125}
126
127void IntegerConflictResolution::AddToQueue(
128 GlobalTrailIndex source_index, const IntegerReason& reason,
129 std::vector<SatClause*>* subsumed_clauses) {
130 ++num_expansions_;
131
132 // If we have a linear reason with slack, check to see if we can relax the
133 // reason and have more slack, because we don't need to explain the stronger
134 // possible push that was done.
135 //
136 // TODO(user): Skip for the first AddToQueue() that correspond to a conflict.
137 // Or handle properly, for now, we never have !vars.empty() for conflicts.
138 IntegerValue slack = reason.slack;
139 if (!reason.vars.empty()) {
140 const IntegerLiteral propagated_i_lit = reason.propagated_i_lit;
141 const IntegerVariable var = propagated_i_lit.var;
142
143 IntegerValue needed_bound = kMaxIntegerValue;
144 if (source_index.IsInteger()) {
145 CHECK_LE(reason.index_at_propagation, source_index.integer_index);
146 CHECK_EQ(var,
147 integer_trail_->IntegerLiteralAtIndex(source_index.integer_index)
148 .var);
149 IntegerVariableData& data = int_data_[var];
150 needed_bound = data.bound;
151 } else {
152 // Currently the only other case where we have a linear reason is for
153 // associated literals, in which case, we just need to explain the
154 // associated bound, which might be lower than what is currently
155 // explained.
156 for (const IntegerLiteral i_lit : IndexToIntegerLiterals(source_index)) {
157 if (i_lit.var == var) {
158 // In some corner case we see the same variable more than once!
159 needed_bound = std::min(i_lit.bound, needed_bound);
160 }
161 }
162
163 // Special case for initial conflict.
164 // TODO(user): We can relax more in this case.
165 if (source_index.bool_index == trail_->Index()) {
166 needed_bound = propagated_i_lit.bound;
167 }
168 CHECK_NE(needed_bound, kMaxIntegerValue);
169 }
170
171 // If we have holes, and var >= needed_bound falls into one, we can relax
172 // it as much as possible.
173 //
174 // Note that this is needed for the check needed_bound <= propagated_bound.
175 needed_bound = RelaxBoundIfHoles(var, needed_bound);
176 CHECK_LE(needed_bound, propagated_i_lit.bound);
177
178 // TODO(user): It might be better to pass to the Explain() function the
179 // thing we need to be explaining, and let it handle the modification of
180 // the slack. So we can also relax non-linear reason.
181 if (needed_bound < propagated_i_lit.bound) {
182 IntegerValue coeff = 0;
183 for (int i = 0; i < reason.vars.size(); ++i) {
184 if (reason.vars[i] == NegationOf(propagated_i_lit.var)) {
185 coeff = reason.coeffs[i];
186 break;
187 }
188 }
189 CHECK_GT(coeff, 0); // Should always be positive.
190
191 // Bump the slack !
192 ++num_slack_relax_;
193 slack = CapAddI(slack,
194 CapProdI(coeff, propagated_i_lit.bound - needed_bound));
195 }
196 }
197
198 // Reset.
199 // As we explain var >= bound, we might need var >= lower_bound.
200 for (const IntegerLiteral i_lit : IndexToIntegerLiterals(source_index)) {
201 IntegerVariableData& data = int_data_[i_lit.var];
202 if (i_lit.bound >= data.bound) {
203 data.bound = kMinIntegerValue;
204 }
205 }
206
207 for (const auto span_of_literals :
208 {reason.boolean_literals_at_true, reason.boolean_literals_at_false}) {
209 for (const Literal literal : span_of_literals) {
210 const auto& info = trail_->Info(literal.Variable());
211 if (info.level == 0) continue;
212 if (tmp_bool_index_seen_[info.trail_index]) continue;
213
214 subsumed_clauses->clear();
215 tmp_bool_index_seen_.Set(info.trail_index);
216
217 const GlobalTrailIndex index{info.level, info.trail_index};
218 tmp_queue_.push_back(index);
219 }
220 }
221 for (const IntegerLiteral i_lit : reason.integer_literals) {
222 ProcessIntegerLiteral(source_index, i_lit);
223 }
224
225 // Deal with linear reason.
226 // TODO(user): The support for that could be improved.
227 // In particular, we can sort in order to process slack in a good heuristic
228 // order.
229 if (reason.vars.empty()) return;
230
231 const int size = reason.vars.size();
232 const IntegerVariable to_ignore =
233 PositiveVariable(reason.propagated_i_lit.var);
234 for (int i = 0; i < size; ++i) {
235 const IntegerVariable var = reason.vars[i];
236 if (PositiveVariable(var) == to_ignore) continue;
237
238 IntegerVariableData& data = int_data_[var];
239 if (!data.in_queue) {
240 data.int_index_in_queue = integer_trail_->GetFirstIndexBefore(
241 var, source_index, data.int_index_in_queue);
242 if (data.int_index_in_queue < 0) continue; // root level.
243
244 data.in_queue = true;
245 tmp_queue_.push_back(
246 integer_trail_->GlobalIndexAt(data.int_index_in_queue));
247 }
248
249 CHECK_LT(integer_trail_->GlobalIndexAt(data.int_index_in_queue),
250 source_index);
251
252 // In all case, we need the bound at the time.
253 // in some rare case, we have reason.index_at_propagation <
254 // data.int_index_in_queue So we might use a stronger integer literal than
255 // necessary. Investigate further.
256 if (data.int_index_in_queue > reason.index_at_propagation) {
257 ++num_possibly_non_optimal_reason_;
258 }
259
260 IntegerValue required_bound =
261 integer_trail_->IntegerLiteralAtIndex(data.int_index_in_queue).bound;
262
263 CHECK_GE(required_bound, data.bound);
264 if (slack > 0 && required_bound > data.bound) {
265 CHECK_GT(reason.coeffs[i], 0);
266 IntegerValue delta = FloorRatio(slack, reason.coeffs[i]);
267 delta = std::min(delta, CapSubI(required_bound, data.bound));
268 if (delta > 0) {
269 ++num_slack_usage_;
270 required_bound -= delta;
271 slack -= reason.coeffs[i] * delta;
272 }
273 }
274
275 data.bound = required_bound;
276 }
277}
278
279void IntegerConflictResolution::ProcessIntegerLiteral(
280 GlobalTrailIndex source_index, IntegerLiteral i_lit) {
281 CHECK(!i_lit.IsAlwaysFalse());
282 if (i_lit.IsAlwaysTrue()) return;
283
284 DCHECK_GE(i_lit.var, 0);
285 DCHECK_LT(i_lit.var, int_data_.size());
286 if (i_lit.bound <= tmp_var_to_settled_lb_[i_lit.var]) return;
287 if (i_lit.bound <= integer_trail_->LevelZeroLowerBound(i_lit.var)) return;
288 DCHECK_LE(i_lit.bound, integer_trail_->LowerBound(i_lit.var));
289
290 IntegerVariableData& data = int_data_[i_lit.var];
291
292 if (!data.in_queue) {
293 // Initialize if we never saw it before.
294 data.int_index_in_queue = integer_trail_->GetFirstIndexBefore(
295 i_lit.var, source_index, data.int_index_in_queue);
296
297 if (data.int_index_in_queue < 0) return; // root level.
298 data.in_queue = true;
299 tmp_queue_.push_back(
300 integer_trail_->GlobalIndexAt(data.int_index_in_queue));
301 }
302
303 data.bound = std::max(data.bound, i_lit.bound);
304 CHECK_LE(data.bound,
305 integer_trail_->IntegerLiteralAtIndex(data.int_index_in_queue).bound)
306 << " " << i_lit.bound;
307}
308
310 std::vector<Literal>* conflict,
311 std::vector<Literal>* reason_used_to_infer_the_conflict,
312 std::vector<SatClause*>* subsumed_clauses) {
313 const int old_conflict_size = conflict->size();
314 if (old_conflict_size > 0) {
315 comparison_old_sum_of_literals_ += old_conflict_size;
316 comparison_old_num_subsumed_ += subsumed_clauses->size();
317 }
318
319 conflict->clear();
320 reason_used_to_infer_the_conflict->clear();
321 subsumed_clauses->clear();
322
323 // WARNING: This is not valid after further GetIntegerReason() calls.
324 const IntegerReason& starting_conflict = integer_trail_->IntegerConflict();
325 if (starting_conflict.empty()) return;
326
327 // Clear data.
328 // TODO(user): Sparse clear.
329 const int num_i_vars = integer_trail_->NumIntegerVariables().value();
330 int_data_.clear();
331 int_data_.resize(num_i_vars);
332 // Note the +1 in case we create a new 1-UIP boolean.
333 tmp_bool_index_seen_.ClearAndResize(trail_->Index() + 1);
334 tmp_var_to_settled_lb_.assign(num_i_vars, kMinIntegerValue);
335
336 tmp_queue_.clear();
337 AddToQueue(GlobalTrailIndex{trail_->CurrentDecisionLevel(), trail_->Index()},
338 starting_conflict, subsumed_clauses);
339 std::make_heap(tmp_queue_.begin(), tmp_queue_.end());
340
341 // We will expand Booleans as long as we don't have first UIP.
342 // Then we will expand all integer_literal until we have only Boolean left.
343 int64_t work_done = 0;
344 bool uip_found = false;
345 while (!tmp_queue_.empty()) {
346 ++work_done;
347
348 // TODO(user): By looking at the reason, we can "correct" the level and
349 // trail index, if it changes, we could update the queue and continue. This
350 // is however harder to do now that we use bounds for the reason not
351 // indices.
352 GlobalTrailIndex top_index = tmp_queue_.front();
353 std::pop_heap(tmp_queue_.begin(), tmp_queue_.end());
354 tmp_queue_.pop_back();
355
356 const bool is_only_one_left_at_top_level =
357 tmp_queue_.empty() || tmp_queue_.front().level < top_index.level;
358
359 if (top_index.IsInteger()) {
360 const IntegerLiteral i_lit =
361 integer_trail_->IntegerLiteralAtIndex(top_index.integer_index);
362 IntegerVariableData& data = int_data_[i_lit.var];
363 const IntegerValue bound_to_explain = data.bound;
364 CHECK(data.in_queue);
365 CHECK_EQ(data.int_index_in_queue, top_index.integer_index);
366 CHECK_LE(data.bound, i_lit.bound);
367
368 // Skip until next time we need this variable.
369 if (data.bound <= tmp_var_to_settled_lb_[i_lit.var] ||
370 data.bound <= integer_trail_->LevelZeroLowerBound(i_lit.var) ||
371 data.int_index_in_queue < num_i_vars) {
372 data.in_queue = false;
373 data.bound = kMinIntegerValue;
374 continue;
375 }
376
377 const int previous_index =
378 integer_trail_->PreviousTrailIndex(top_index.integer_index);
379 if (data.bound < i_lit.bound) {
380 if (previous_index >= 0) {
381 const IntegerLiteral previous_i_lit =
382 integer_trail_->IntegerLiteralAtIndex(previous_index);
383 if (data.bound <= previous_i_lit.bound) {
384 // The previous integer entry can explain our data.bound,
385 // re-enqueue until next time.
386 data.int_index_in_queue = previous_index;
387 tmp_queue_.push_back(
388 integer_trail_->GlobalIndexAt(data.int_index_in_queue));
389 CHECK_LE(
390 data.bound,
391 integer_trail_->IntegerLiteralAtIndex(data.int_index_in_queue)
392 .bound);
393 std::push_heap(tmp_queue_.begin(), tmp_queue_.end());
394 continue;
395 }
396 } else {
397 // Remove.
398 // This variable shouldn't be needed anymore.
399 data.int_index_in_queue = previous_index;
400 data.in_queue = false;
401 data.bound = kMinIntegerValue;
402 continue;
403 }
404 }
405
406 // We are going to expand the reason at top_index, clear the data for
407 // future reasons.
408 data.int_index_in_queue = previous_index;
409 data.in_queue = false;
410
411 // Optional. Try to see if we have a good enough associated
412 // integer_literal. This can be disabled, but it should lead to better
413 // reason hopefully.
414 if (is_only_one_left_at_top_level || uip_found) {
415 // We only need to explain var >= bound_to_explain.
416 // We have the explanation for i_lit.
417 IntegerValue associated_bound;
418 const LiteralIndex lit_index =
419 integer_encoder_->SearchForLiteralAtOrAfter(
420 IntegerLiteral::GreaterOrEqual(i_lit.var, bound_to_explain),
421 &associated_bound);
422 if (lit_index != kNoLiteralIndex &&
423 associated_bound >= bound_to_explain &&
424 associated_bound <= i_lit.bound) {
425 const Literal lit(lit_index);
426
427 IntegerValue test_bound;
428 const LiteralIndex test_index =
429 integer_encoder_->SearchForLiteralAtOrBefore(i_lit, &test_bound);
430 if (test_index != kNoLiteralIndex)
431 CHECK_LE(associated_bound, test_bound);
432
433 // Lets do more sanity_check before just using this literal.
434 // Instead. Since we output it right away. we should be good.
435 const auto& info = trail_->Info(lit.Variable());
436 if (trail_->Assignment().LiteralIsTrue(lit) &&
437 info.level == top_index.level) {
438 // Note that we don't always have new_top >= top_index, this is fine
439 // we can still use this Boolean in the final output.
440 if (tmp_bool_index_seen_[info.trail_index]) {
441 data.bound = kMinIntegerValue;
442 ++num_associated_literal_use_;
443 continue;
444 }
445 const GlobalTrailIndex new_top{info.level, info.trail_index};
446 tmp_bool_index_seen_.Set(info.trail_index);
447
448 // TODO(user): Not sure some corner cases still allow subsumption.
449 subsumed_clauses->clear();
450
451 data.bound = kMinIntegerValue;
452 top_index = new_top;
453 ++num_associated_literal_use_;
454 } else {
455 ++num_associated_literal_fail_;
456 }
457 } else if (params_.create_1uip_boolean_during_icr() &&
458 top_index.level > sat_solver_->AssumptionLevel() &&
459 is_only_one_left_at_top_level && !uip_found) {
460 // Lets create a new associated literal and use it as the UIP.
461 //
462 // TODO(user): Note that we disabled this with assumptions otherwise
463 // we might have a core with new literal !
464 const Literal new_lit =
465 integer_encoder_->GetOrCreateAssociatedLiteral(
466 IntegerLiteral::GreaterOrEqual(i_lit.var, bound_to_explain));
467
468 // This should always be true.
469 // TODO(user): Why is it assigned sometimes and not always?
470 if (!trail_->Assignment().LiteralIsAssigned(new_lit)) {
471 // Using a decision should work as we will backtrack right away.
472 trail_->EnqueueSearchDecision(new_lit);
473 }
474
475 // It should be true.
476 CHECK(trail_->Assignment().LiteralIsTrue(new_lit))
477 << associated_bound << " " << bound_to_explain << " "
478 << i_lit.bound << " " << lit_index << " new " << new_lit;
479
480 const auto& info = trail_->Info(new_lit.Variable());
481 CHECK_GE(info.level, top_index.level);
482 CHECK_EQ((*trail_)[info.trail_index], new_lit);
483 const GlobalTrailIndex new_top{info.level, info.trail_index};
484
485 tmp_bool_index_seen_.Set(info.trail_index);
486 subsumed_clauses->clear();
487 data.bound = kMinIntegerValue;
488
489 top_index = new_top;
490 ++num_created_1uip_bool_;
491 }
492 }
493 }
494
495 if (top_index.IsBoolean()) {
496 const Literal literal = (*trail_)[top_index.bool_index];
497
498 // Do we have a single GlobalTrailIndex at the top assignment level ?
499 if (top_index.level <= sat_solver_->AssumptionLevel()) {
500 // This will just output all Booleans from the assumption level.
501 uip_found = true;
502 }
503 if (!uip_found) {
504 if (is_only_one_left_at_top_level) {
505 if (top_index.level < trail_->CurrentDecisionLevel()) {
506 ++num_conflicts_at_wrong_level_;
507 }
508
509 // Only one Boolean at max-level, we have the first UIP.
510 uip_found = true;
511 }
512 }
513
514 if (uip_found) {
515 if (params_.binary_minimization_algorithm() !=
517 if (conflict->empty()) {
518 // This one will always stay in the conflict, even after
519 // minimization. So we can use it to minimize the conflict and avoid
520 // some further expansion.
521 for (const Literal l :
522 implications_->GetAllImpliedLiterals(literal)) {
523 for (const IntegerLiteral i_lit :
524 integer_encoder_->GetIntegerLiterals(l)) {
525 // The std::max() is for the corner case of more than one
526 // integer literal on the same variable.
527 //
528 // TODO(user): we should probably make sure this never happen
529 // instead.
530 tmp_var_to_settled_lb_[i_lit.var] =
531 std::max(tmp_var_to_settled_lb_[i_lit.var], i_lit.bound);
532 ++num_associated_integer_for_literals_in_conflict_;
533 }
534 }
535 } else {
536 // This assumes no-one call GetAllImpliedLiterals() while we
537 // run this algorithm, and that the info stays valid as we create
538 // new literal.
539 if (implications_->LiteralIsImplied(literal)) {
540 ++num_binary_minimization_;
541 continue;
542 }
543 }
544 }
545
546 // Note that we will fill conflict in reverse order of GlobalTrailIndex.
547 // So the first-UIP will be first, this is required by the sat solver.
548 conflict->push_back(literal.Negated());
549
550 // This literal is staying in the final conflict. If it has associated
551 // integer_literal, then these integer literals will be true for all the
552 // subsequent resolution. We can exploit that.
553 for (const IntegerLiteral i_lit :
554 integer_encoder_->GetIntegerLiterals(literal)) {
555 // The std::max() is for the corner case of more than one integer
556 // literal on the same variable.
557 // TODO(user): we should probably make sure this never happen instead.
558 tmp_var_to_settled_lb_[i_lit.var] =
559 std::max(tmp_var_to_settled_lb_[i_lit.var], i_lit.bound);
560 ++num_associated_integer_for_literals_in_conflict_;
561 }
562
563 continue;
564 }
565
566 // We will expand this Boolean.
567 CHECK_NE(trail_->Info(literal.Variable()).type,
569 << DebugGlobalIndex(top_index)
570 << " before: " << DebugGlobalIndex(tmp_queue_.front());
571 reason_used_to_infer_the_conflict->push_back(literal);
572 } else {
573 // Skip stale integer entry.
574 const IntegerLiteral i_lit =
575 integer_trail_->IntegerLiteralAtIndex(top_index.integer_index);
576 if (tmp_var_to_settled_lb_[i_lit.var] >= i_lit.bound) continue;
577 }
578
579 std::optional<IntegerValue> needed_bound;
580 if (top_index.IsInteger()) {
581 const IntegerVariable var =
582 integer_trail_->IntegerLiteralAtIndex(top_index.integer_index).var;
583 needed_bound = RelaxBoundIfHoles(var, int_data_[var].bound);
584 }
585
586 // Expand.
587 //
588 // TODO(user): There is probably a faster way to recover the heap propety
589 // than doing it one by one.
590 const int old_size = tmp_queue_.size();
591 AddToQueue(top_index,
592 integer_trail_->GetIntegerReason(top_index, needed_bound),
593 subsumed_clauses);
594 for (int i = old_size + 1; i <= tmp_queue_.size(); ++i) {
595 std::push_heap(tmp_queue_.begin(), tmp_queue_.begin() + i);
596 }
597
598 // Subsumption ?
599 // We will check at the end, but also filter the list each time we have
600 // a new Boolean in the conflict.
601 if (top_index.IsBoolean()) {
602 // Tricky: info.type might not be the same as AssignmentType().
603 const Literal literal = (*trail_)[top_index.bool_index];
604 if (trail_->AssignmentType(literal.Variable()) ==
605 clauses_propagator_->PropagatorId()) {
606 const AssignmentInfo& info = trail_->Info(literal.Variable());
607 SatClause* clause = clauses_propagator_->ReasonClause(info.trail_index);
608 subsumed_clauses->push_back(clause);
609 }
610 }
611 }
612
613 num_conflict_literals_ += conflict->size();
614 FilterSubsumedClauses(conflict, subsumed_clauses);
615
616 if (old_conflict_size > 0) {
617 if (conflict->size() < old_conflict_size) {
618 ++comparison_num_win_;
619 } else if (conflict->size() > old_conflict_size) {
620 ++comparison_num_loose_;
621 } else {
622 ++comparison_num_same_;
623 }
624 }
625}
626
627void IntegerConflictResolution::FilterSubsumedClauses(
628 std::vector<Literal>* conflict, std::vector<SatClause*>* subsumed_clauses) {
629 tmp_bool_seen_.ClearAndResize(BooleanVariable(trail_->NumVariables()));
630 for (const Literal l : *conflict) tmp_bool_seen_.Set(l.Variable());
631
632 int new_size = 0;
633 for (SatClause* clause : *subsumed_clauses) {
634 int intersection_size = 0;
635 for (const Literal l : clause->AsSpan()) {
636 if (tmp_bool_seen_[l.Variable()]) {
637 ++intersection_size;
638 if (intersection_size == conflict->size()) {
639 (*subsumed_clauses)[new_size++] = clause;
640 break;
641 }
642 }
643 }
644 }
645 subsumed_clauses->resize(new_size);
646 num_subsumed_ += new_size;
647}
648
649std::string IntegerConflictResolution::DebugGlobalIndex(
650 GlobalTrailIndex index) {
651 return absl::StrCat(
652 index.level, "|", index.bool_index, "|",
653 index.IsInteger() ? absl::StrCat(index.integer_index) : "", " ",
654 (index.IsBoolean()
655 ? (*trail_)[index.bool_index].DebugString()
656 : integer_trail_->IntegerLiteralAtIndex(index.integer_index)
657 .DebugString()));
658}
659
660std::string IntegerConflictResolution::DebugGlobalIndex(
661 absl::Span<const GlobalTrailIndex> indices) {
662 std::string out = "[";
663 bool first = true;
664 for (const GlobalTrailIndex index : indices) {
665 if (!first) absl::StrAppend(&out, ", ");
666 first = false;
667 absl::StrAppend(&out, DebugGlobalIndex(index));
668 }
669 return absl::StrCat(out, "]");
670}
671
672} // namespace operations_research::sat
void ClearAndResize(IntegerType size)
Definition bitset.h:852
void Set(IntegerType index)
Definition bitset.h:878
void ComputeFirstUIPConflict(std::vector< Literal > *conflict, std::vector< Literal > *reason_used_to_infer_the_conflict, std::vector< SatClause * > *subsumed_clauses)
const InlinedIntegerLiteralVector & GetIntegerLiterals(Literal lit) const
Definition integer.h:229
IntegerLiteral IntegerLiteralAtIndex(int integer_trail_index) const
Definition integer.h:868
BooleanVariable Variable() const
Definition sat_base.h:88
static constexpr BinaryMinizationAlgorithm NO_BINARY_MINIMIZATION
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
const LiteralIndex kNoLiteralIndex(-1)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
Definition integer.cc:52
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
IntegerVariable PositiveVariable(IntegerVariable i)
IntegerValue CapAddI(IntegerValue a, IntegerValue b)
IntegerValue CapSubI(IntegerValue a, IntegerValue b)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)