OR-Tools  9.3
simplification.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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 <deque>
19#include <limits>
20#include <memory>
21#include <utility>
22#include <vector>
23
24#include "absl/container/btree_set.h"
25#include "absl/memory/memory.h"
26#include "absl/types/span.h"
31#include "ortools/base/macros.h"
34#include "ortools/base/timer.h"
37#include "ortools/sat/model.h"
38#include "ortools/sat/probing.h"
41#include "ortools/sat/sat_parameters.pb.h"
46
47namespace operations_research {
48namespace sat {
49
51 : initial_num_variables_(num_variables), num_variables_(num_variables) {
52 reverse_mapping_.resize(num_variables);
53 for (BooleanVariable var(0); var < num_variables; ++var) {
54 reverse_mapping_[var] = var;
55 }
56 assignment_.Resize(num_variables);
57}
58
59void SatPostsolver::Add(Literal x, absl::Span<const Literal> clause) {
60 DCHECK(!clause.empty());
61 DCHECK(std::find(clause.begin(), clause.end(), x) != clause.end());
62 associated_literal_.push_back(ApplyReverseMapping(x));
63 clauses_start_.push_back(clauses_literals_.size());
64 for (const Literal& l : clause) {
65 clauses_literals_.push_back(ApplyReverseMapping(l));
66 }
67}
68
70 const Literal l = ApplyReverseMapping(x);
71 assignment_.AssignFromTrueLiteral(l);
72}
73
77 if (reverse_mapping_.size() < mapping.size()) {
78 // We have new variables.
79 while (reverse_mapping_.size() < mapping.size()) {
80 reverse_mapping_.push_back(BooleanVariable(num_variables_++));
81 }
82 assignment_.Resize(num_variables_);
83 }
84 for (BooleanVariable v(0); v < mapping.size(); ++v) {
85 const BooleanVariable image = mapping[v];
86 if (image != kNoBooleanVariable) {
87 if (image >= new_mapping.size()) {
88 new_mapping.resize(image.value() + 1, kNoBooleanVariable);
89 }
90 new_mapping[image] = reverse_mapping_[v];
91 DCHECK_NE(new_mapping[image], kNoBooleanVariable);
92 }
93 }
94 std::swap(new_mapping, reverse_mapping_);
95}
96
97Literal SatPostsolver::ApplyReverseMapping(Literal l) {
98 if (l.Variable() >= reverse_mapping_.size()) {
99 // We have new variables.
100 while (l.Variable() >= reverse_mapping_.size()) {
101 reverse_mapping_.push_back(BooleanVariable(num_variables_++));
102 }
103 assignment_.Resize(num_variables_);
104 }
105 DCHECK_NE(reverse_mapping_[l.Variable()], kNoBooleanVariable);
106 const Literal result(reverse_mapping_[l.Variable()], l.IsPositive());
107 DCHECK(!assignment_.LiteralIsAssigned(result));
108 return result;
109}
110
111void SatPostsolver::Postsolve(VariablesAssignment* assignment) const {
112 // First, we set all unassigned variable to true.
113 // This will be a valid assignment of the presolved problem.
114 for (BooleanVariable var(0); var < assignment->NumberOfVariables(); ++var) {
115 if (!assignment->VariableIsAssigned(var)) {
116 assignment->AssignFromTrueLiteral(Literal(var, true));
117 }
118 }
119
120 int previous_start = clauses_literals_.size();
121 for (int i = static_cast<int>(clauses_start_.size()) - 1; i >= 0; --i) {
122 bool set_associated_var = true;
123 const int new_start = clauses_start_[i];
124 for (int j = new_start; j < previous_start; ++j) {
125 if (assignment->LiteralIsTrue(clauses_literals_[j])) {
126 set_associated_var = false;
127 break;
128 }
129 }
130 previous_start = new_start;
131 if (set_associated_var) {
132 assignment->UnassignLiteral(associated_literal_[i].Negated());
133 assignment->AssignFromTrueLiteral(associated_literal_[i]);
134 }
135 }
136
137 // Ignore the value of any variables added by the presolve.
138 assignment->Resize(initial_num_variables_);
139}
140
142 const SatSolver& solver) {
143 std::vector<bool> solution(solver.NumVariables());
144 for (BooleanVariable var(0); var < solver.NumVariables(); ++var) {
146 solution[var.value()] =
147 solver.Assignment().LiteralIsTrue(Literal(var, true));
148 }
149 return PostsolveSolution(solution);
150}
151
153 const std::vector<bool>& solution) {
154 for (BooleanVariable var(0); var < solution.size(); ++var) {
155 DCHECK_LT(var, reverse_mapping_.size());
156 DCHECK_NE(reverse_mapping_[var], kNoBooleanVariable);
157 DCHECK(!assignment_.VariableIsAssigned(reverse_mapping_[var]));
158 assignment_.AssignFromTrueLiteral(
159 Literal(reverse_mapping_[var], solution[var.value()]));
160 }
161 Postsolve(&assignment_);
162 std::vector<bool> postsolved_solution;
163 postsolved_solution.reserve(initial_num_variables_);
164 for (int i = 0; i < initial_num_variables_; ++i) {
165 postsolved_solution.push_back(
166 assignment_.LiteralIsTrue(Literal(BooleanVariable(i), true)));
167 }
168 return postsolved_solution;
169}
170
172
173void SatPresolver::AddClause(absl::Span<const Literal> clause) {
174 DCHECK_GT(clause.size(), 0) << "Added an empty clause to the presolver";
175 const ClauseIndex ci(clauses_.size());
176 clauses_.push_back(std::vector<Literal>(clause.begin(), clause.end()));
177 in_clause_to_process_.push_back(true);
178 clause_to_process_.push_back(ci);
179
180 bool changed = false;
181 std::vector<Literal>& clause_ref = clauses_.back();
182 if (!equiv_mapping_.empty()) {
183 for (int i = 0; i < clause_ref.size(); ++i) {
184 const Literal old_literal = clause_ref[i];
185 clause_ref[i] = Literal(equiv_mapping_[clause_ref[i].Index()]);
186 if (old_literal != clause_ref[i]) changed = true;
187 }
188 }
189 std::sort(clause_ref.begin(), clause_ref.end());
190 clause_ref.erase(std::unique(clause_ref.begin(), clause_ref.end()),
191 clause_ref.end());
192
193 // Check for trivial clauses:
194 for (int i = 1; i < clause_ref.size(); ++i) {
195 if (clause_ref[i] == clause_ref[i - 1].Negated()) {
196 // The clause is trivial!
197 ++num_trivial_clauses_;
198 clause_to_process_.pop_back();
199 clauses_.pop_back();
200 in_clause_to_process_.pop_back();
201 return;
202 }
203 }
204
205 // This needs to be done after the basic canonicalization above.
206 signatures_.push_back(ComputeSignatureOfClauseVariables(ci));
207 DCHECK_EQ(signatures_.size(), clauses_.size());
208
209 if (drat_proof_handler_ != nullptr && changed) {
210 drat_proof_handler_->AddClause(clause_ref);
211 drat_proof_handler_->DeleteClause(clause);
212 }
213
214 const Literal max_literal = clause_ref.back();
215 const int required_size = std::max(max_literal.Index().value(),
216 max_literal.NegatedIndex().value()) +
217 1;
218 if (required_size > literal_to_clauses_.size()) {
219 literal_to_clauses_.resize(required_size);
220 literal_to_clause_sizes_.resize(required_size);
221 }
222 for (Literal e : clause_ref) {
223 literal_to_clauses_[e.Index()].push_back(ci);
224 literal_to_clause_sizes_[e.Index()]++;
225 }
226}
227
228void SatPresolver::SetNumVariables(int num_variables) {
229 const int required_size = 2 * num_variables;
230 if (required_size > literal_to_clauses_.size()) {
231 literal_to_clauses_.resize(required_size);
232 literal_to_clause_sizes_.resize(required_size);
233 }
234}
235
236void SatPresolver::AddClauseInternal(std::vector<Literal>* clause) {
237 if (drat_proof_handler_ != nullptr) drat_proof_handler_->AddClause(*clause);
238
239 DCHECK(std::is_sorted(clause->begin(), clause->end()));
240 DCHECK_GT(clause->size(), 0) << "TODO(user): Unsat during presolve?";
241 const ClauseIndex ci(clauses_.size());
242 clauses_.push_back(std::vector<Literal>());
243 clauses_.back().swap(*clause);
244 in_clause_to_process_.push_back(true);
245 clause_to_process_.push_back(ci);
246 for (const Literal e : clauses_.back()) {
247 literal_to_clauses_[e.Index()].push_back(ci);
248 literal_to_clause_sizes_[e.Index()]++;
249 UpdatePriorityQueue(e.Variable());
250 UpdateBvaPriorityQueue(e.Index());
251 }
252
253 signatures_.push_back(ComputeSignatureOfClauseVariables(ci));
254 DCHECK_EQ(signatures_.size(), clauses_.size());
255}
256
260 BooleanVariable new_var(0);
261 for (BooleanVariable var(0); var < NumVariables(); ++var) {
262 if (literal_to_clause_sizes_[Literal(var, true).Index()] > 0 ||
263 literal_to_clause_sizes_[Literal(var, false).Index()] > 0) {
264 result.push_back(new_var);
265 ++new_var;
266 } else {
268 }
269 }
270 return result;
271}
272
274 // Cleanup some memory that is not needed anymore. Note that we do need
275 // literal_to_clause_sizes_ for VariableMapping() to work.
276 var_pq_.Clear();
277 var_pq_elements_.clear();
278 in_clause_to_process_.clear();
279 clause_to_process_.clear();
280 literal_to_clauses_.clear();
281 signatures_.clear();
282
285 int new_size = 0;
286 for (BooleanVariable index : mapping) {
287 if (index != kNoBooleanVariable) ++new_size;
288 }
289
290 std::vector<Literal> temp;
291 solver->SetNumVariables(new_size);
292 for (std::vector<Literal>& clause_ref : clauses_) {
293 temp.clear();
294 for (Literal l : clause_ref) {
296 temp.push_back(Literal(mapping[l.Variable()], l.IsPositive()));
297 }
298 if (!temp.empty()) solver->AddProblemClause(temp);
299 gtl::STLClearObject(&clause_ref);
300 }
301}
302
303bool SatPresolver::ProcessAllClauses() {
304 int num_skipped_checks = 0;
305 const int kCheckFrequency = 1000;
306
307 // Because on large problem we don't have a budget to process all clauses,
308 // lets start by the smallest ones first.
309 std::sort(clause_to_process_.begin(), clause_to_process_.end(),
310 [this](ClauseIndex c1, ClauseIndex c2) {
311 return clauses_[c1].size() < clauses_[c2].size();
312 });
313 while (!clause_to_process_.empty()) {
314 const ClauseIndex ci = clause_to_process_.front();
315 in_clause_to_process_[ci] = false;
316 clause_to_process_.pop_front();
317 if (!ProcessClauseToSimplifyOthers(ci)) return false;
318 if (++num_skipped_checks >= kCheckFrequency) {
319 if (num_inspected_signatures_ + num_inspected_literals_ > 1e9) {
320 VLOG(1) << "Aborting ProcessAllClauses() because work limit has been "
321 "reached";
322 return true;
323 }
324 if (time_limit_ != nullptr && time_limit_->LimitReached()) return true;
325 num_skipped_checks = 0;
326 }
327 }
328 return true;
329}
330
332 // This is slighlty inefficient, but the presolve algorithm is
333 // a lot more costly anyway.
334 std::vector<bool> can_be_removed(NumVariables(), true);
335 return Presolve(can_be_removed);
336}
337
338bool SatPresolver::Presolve(const std::vector<bool>& can_be_removed) {
339 WallTimer timer;
340 timer.Start();
341
342 if (logger_->LoggingIsEnabled()) {
343 int64_t num_removable = 0;
344 for (const bool b : can_be_removed) {
345 if (b) ++num_removable;
346 }
347 SOLVER_LOG(logger_,
348 "[SAT presolve] num removable Booleans: ", num_removable, " / ",
349 can_be_removed.size());
350 SOLVER_LOG(logger_,
351 "[SAT presolve] num trivial clauses: ", num_trivial_clauses_);
352 DisplayStats(0);
353 }
354
355 // TODO(user): When a clause is strengthened, add it to a queue so it can
356 // be processed again?
357 if (!ProcessAllClauses()) return false;
358 if (logger_->LoggingIsEnabled()) DisplayStats(timer.Get());
359
360 if (time_limit_ != nullptr && time_limit_->LimitReached()) return true;
361 if (num_inspected_signatures_ + num_inspected_literals_ > 1e9) return true;
362
363 InitializePriorityQueue();
364 while (var_pq_.Size() > 0) {
365 const BooleanVariable var = var_pq_.Top()->variable;
366 var_pq_.Pop();
367 if (!can_be_removed[var.value()]) continue;
368 if (CrossProduct(Literal(var, true))) {
369 if (!ProcessAllClauses()) return false;
370 }
371 if (time_limit_ != nullptr && time_limit_->LimitReached()) return true;
372 if (num_inspected_signatures_ + num_inspected_literals_ > 1e9) return true;
373 }
374 if (logger_->LoggingIsEnabled()) DisplayStats(timer.Get());
375
376 // We apply BVA after a pass of the other algorithms.
377 if (parameters_.presolve_use_bva()) {
379 if (logger_->LoggingIsEnabled()) DisplayStats(timer.Get());
380 }
381
382 return true;
383}
384
386 var_pq_elements_.clear(); // so we don't update it.
387 InitializeBvaPriorityQueue();
388 while (bva_pq_.Size() > 0) {
389 const LiteralIndex lit = bva_pq_.Top()->literal;
390 bva_pq_.Pop();
391 SimpleBva(lit);
392 }
393}
394
395// We use the same notation as in the article mentioned in the .h
396void SatPresolver::SimpleBva(LiteralIndex l) {
397 literal_to_p_size_.resize(literal_to_clauses_.size(), 0);
398 DCHECK(std::all_of(literal_to_p_size_.begin(), literal_to_p_size_.end(),
399 [](int v) { return v == 0; }));
400
401 // We will try to add a literal to m_lit_ and take a subset of m_cls_ such
402 // that |m_lit_| * |m_cls_| - |m_lit_| - |m_cls_| is maximized.
403 m_lit_ = {l};
404 m_cls_ = literal_to_clauses_[l];
405
406 int reduction = 0;
407 while (true) {
408 LiteralIndex lmax = kNoLiteralIndex;
409 int max_size = 0;
410
411 flattened_p_.clear();
412 for (const ClauseIndex c : m_cls_) {
413 const std::vector<Literal>& clause = clauses_[c];
414 if (clause.empty()) continue; // It has been deleted.
415
416 // Find a literal different from l that occur in the less number of
417 // clauses.
418 const LiteralIndex l_min =
419 FindLiteralWithShortestOccurrenceListExcluding(clause, Literal(l));
420 if (l_min == kNoLiteralIndex) continue;
421
422 // Find all the clauses of the form "clause \ {l} + {l'}", for a literal
423 // l' that is not in the clause.
424 for (const ClauseIndex d : literal_to_clauses_[l_min]) {
425 if (clause.size() != clauses_[d].size()) continue;
426 const LiteralIndex l_diff =
427 DifferAtGivenLiteral(clause, clauses_[d], Literal(l));
428 if (l_diff == kNoLiteralIndex || m_lit_.count(l_diff) > 0) continue;
429 if (l_diff == Literal(l).NegatedIndex()) {
430 // Self-subsumbtion!
431 //
432 // TODO(user): Not sure this can happen after the presolve we did
433 // before calling SimpleBva().
434 VLOG(1) << "self-subsumbtion";
435 }
436
437 flattened_p_.push_back({l_diff, c});
438 const int new_size = ++literal_to_p_size_[l_diff];
439 if (new_size > max_size) {
440 lmax = l_diff;
441 max_size = new_size;
442 }
443 }
444 }
445
446 if (lmax == kNoLiteralIndex) break;
447 const int new_m_lit_size = m_lit_.size() + 1;
448 const int new_m_cls_size = max_size;
449 const int new_reduction =
450 new_m_lit_size * new_m_cls_size - new_m_cls_size - new_m_lit_size;
451
452 if (new_reduction <= reduction) break;
453 DCHECK_NE(1, new_m_lit_size);
454 DCHECK_NE(1, new_m_cls_size);
455
456 // TODO(user): Instead of looping and recomputing p_ again, we can instead
457 // simply intersect the clause indices in p_. This should be a lot faster.
458 // That said, we loop again only when we have a reduction, so this happens
459 // not that often compared to the initial computation of p.
460 reduction = new_reduction;
461 m_lit_.insert(lmax);
462
463 // Set m_cls_ to p_[lmax].
464 m_cls_.clear();
465 for (const auto& entry : flattened_p_) {
466 literal_to_p_size_[entry.first] = 0;
467 if (entry.first == lmax) m_cls_.push_back(entry.second);
468 }
469 flattened_p_.clear();
470 }
471
472 // Make sure literal_to_p_size_ is all zero.
473 for (const auto& entry : flattened_p_) literal_to_p_size_[entry.first] = 0;
474 flattened_p_.clear();
475
476 // A strictly positive reduction means that applying the BVA transform will
477 // reduce the overall number of clauses by that much. Here we can control
478 // what kind of reduction we want to apply.
479 if (reduction <= parameters_.presolve_bva_threshold()) return;
480 DCHECK_GT(m_lit_.size(), 1);
481
482 // Create a new variable.
483 const int old_size = literal_to_clauses_.size();
484 const LiteralIndex x_true = LiteralIndex(old_size);
485 const LiteralIndex x_false = LiteralIndex(old_size + 1);
486 literal_to_clauses_.resize(old_size + 2);
487 literal_to_clause_sizes_.resize(old_size + 2);
488 bva_pq_elements_.resize(old_size + 2);
489 bva_pq_elements_[x_true.value()].literal = x_true;
490 bva_pq_elements_[x_false.value()].literal = x_false;
491
492 // Add the new clauses.
493 if (drat_proof_handler_ != nullptr) drat_proof_handler_->AddOneVariable();
494 for (const LiteralIndex lit : m_lit_) {
495 tmp_new_clause_ = {Literal(lit), Literal(x_true)};
496 AddClauseInternal(&tmp_new_clause_);
497 }
498 for (const ClauseIndex ci : m_cls_) {
499 tmp_new_clause_ = clauses_[ci];
500 DCHECK(!tmp_new_clause_.empty());
501 for (Literal& ref : tmp_new_clause_) {
502 if (ref.Index() == l) {
503 ref = Literal(x_false);
504 break;
505 }
506 }
507
508 // TODO(user): we can be more efficient here since the clause used to
509 // derive this one is already sorted. We just need to insert x_false in
510 // the correct place and remove l.
511 std::sort(tmp_new_clause_.begin(), tmp_new_clause_.end());
512 AddClauseInternal(&tmp_new_clause_);
513 }
514
515 // Delete the old clauses.
516 //
517 // TODO(user): do that more efficiently? we can simply store the clause d
518 // instead of finding it again. That said, this is executed only when a
519 // reduction occur, whereas the start of this function occur all the time, so
520 // we want it to be as fast as possible.
521 for (const ClauseIndex c : m_cls_) {
522 const std::vector<Literal>& clause = clauses_[c];
523 DCHECK(!clause.empty());
524 const LiteralIndex l_min =
525 FindLiteralWithShortestOccurrenceListExcluding(clause, Literal(l));
526 for (const LiteralIndex lit : m_lit_) {
527 if (lit == l) continue;
528 for (const ClauseIndex d : literal_to_clauses_[l_min]) {
529 if (clause.size() != clauses_[d].size()) continue;
530 const LiteralIndex l_diff =
531 DifferAtGivenLiteral(clause, clauses_[d], Literal(l));
532 if (l_diff == lit) {
533 Remove(d);
534 break;
535 }
536 }
537 }
538 Remove(c);
539 }
540
541 // Add these elements to the priority queue.
542 //
543 // TODO(user): It seems some of the element already processed could benefit
544 // from being processed again by SimpleBva(). It is unclear if it is worth the
545 // extra time though.
546 AddToBvaPriorityQueue(x_true);
547 AddToBvaPriorityQueue(x_false);
548 AddToBvaPriorityQueue(l);
549}
550
551uint64_t SatPresolver::ComputeSignatureOfClauseVariables(ClauseIndex ci) {
552 uint64_t signature = 0;
553 for (const Literal l : clauses_[ci]) {
554 signature |= (uint64_t{1} << (l.Variable().value() % 64));
555 }
556 DCHECK_EQ(signature == 0, clauses_[ci].empty());
557 return signature;
558}
559
560// We are looking for clause that contains lit and contains a superset of the
561// literals in clauses_[clauses_index] or a superset with just one literal of
562// clauses_[clause_index] negated.
563bool SatPresolver::ProcessClauseToSimplifyOthersUsingLiteral(
564 ClauseIndex clause_index, Literal lit) {
565 const std::vector<Literal>& clause = clauses_[clause_index];
566 const uint64_t clause_signature = signatures_[clause_index];
567 LiteralIndex opposite_literal;
568
569 // Try to simplify the clauses containing 'lit'. We take advantage of this
570 // loop to also detect if there is any empty clause, in which case we will
571 // trigger a "cleaning" below.
572 bool need_cleaning = false;
573 num_inspected_signatures_ += literal_to_clauses_[lit.Index()].size();
574 for (const ClauseIndex ci : literal_to_clauses_[lit.Index()]) {
575 const uint64_t ci_signature = signatures_[ci];
576
577 // This allows to check for empty clause without fetching the memory at
578 // clause_[ci]. It can have a huge time impact on large problems.
579 DCHECK_EQ(ci_signature, ComputeSignatureOfClauseVariables(ci));
580 if (ci_signature == 0) {
581 need_cleaning = true;
582 continue;
583 }
584
585 // Note that SimplifyClause() can return true only if the variables in
586 // 'a' are a subset of the one in 'b'. We use the signatures to abort
587 // early as a speed optimization.
588 if (ci != clause_index && (clause_signature & ~ci_signature) == 0 &&
589 SimplifyClause(clause, &clauses_[ci], &opposite_literal,
590 &num_inspected_literals_)) {
591 if (opposite_literal == kNoLiteralIndex) {
592 need_cleaning = true;
593 Remove(ci);
594 continue;
595 } else {
596 DCHECK_NE(opposite_literal, lit.Index());
597 if (clauses_[ci].empty()) return false; // UNSAT.
598 if (drat_proof_handler_ != nullptr) {
599 // TODO(user): remove the old clauses_[ci] afterwards.
600 drat_proof_handler_->AddClause(clauses_[ci]);
601 }
602
603 // Recompute signature.
604 signatures_[ci] = ComputeSignatureOfClauseVariables(ci);
605
606 // Remove ci from the occurrence list. Note that opposite_literal
607 // cannot be literal or literal.Negated().
608 gtl::STLEraseAllFromSequence(&literal_to_clauses_[opposite_literal],
609 ci);
610 --literal_to_clause_sizes_[opposite_literal];
611 UpdatePriorityQueue(Literal(opposite_literal).Variable());
612
613 if (!in_clause_to_process_[ci]) {
614 in_clause_to_process_[ci] = true;
615 clause_to_process_.push_back(ci);
616 }
617 }
618 }
619 }
620
621 if (need_cleaning) {
622 int new_index = 0;
623 auto& occurrence_list_ref = literal_to_clauses_[lit.Index()];
624 for (const ClauseIndex ci : occurrence_list_ref) {
625 if (signatures_[ci] != 0) occurrence_list_ref[new_index++] = ci;
626 }
627 occurrence_list_ref.resize(new_index);
628 DCHECK_EQ(literal_to_clause_sizes_[lit.Index()], new_index);
629 }
630
631 return true;
632}
633
634// TODO(user): Binary clauses are really common, and we can probably do this
635// more efficiently for them. For instance, we could just take the intersection
636// of two sorted lists to get the simplified clauses.
638 const std::vector<Literal>& clause = clauses_[clause_index];
639 if (clause.empty()) return true;
640 DCHECK(std::is_sorted(clause.begin(), clause.end()));
641
642 LiteralIndex opposite_literal;
643 const Literal lit = FindLiteralWithShortestOccurrenceList(clause);
644
645 if (!ProcessClauseToSimplifyOthersUsingLiteral(clause_index, lit)) {
646 return false;
647 }
648
649 // If there is another "short" occurrence list, use it. Otherwise use the
650 // one corresponding to the negation of lit.
651 const LiteralIndex other_lit_index =
652 FindLiteralWithShortestOccurrenceListExcluding(clause, lit);
653 if (other_lit_index != kNoLiteralIndex &&
654 literal_to_clause_sizes_[other_lit_index] <
655 literal_to_clause_sizes_[lit.NegatedIndex()]) {
656 return ProcessClauseToSimplifyOthersUsingLiteral(clause_index,
657 Literal(other_lit_index));
658 } else {
659 // Treat the clauses containing lit.Negated().
660 int new_index = 0;
661 bool something_removed = false;
662 auto& occurrence_list_ref = literal_to_clauses_[lit.NegatedIndex()];
663 const uint64_t clause_signature = signatures_[clause_index];
664 for (const ClauseIndex ci : occurrence_list_ref) {
665 const uint64_t ci_signature = signatures_[ci];
666 DCHECK_EQ(ci_signature, ComputeSignatureOfClauseVariables(ci));
667 if (ci_signature == 0) continue;
668
669 // TODO(user): not super optimal since we could abort earlier if
670 // opposite_literal is not the negation of shortest_list. Note that this
671 // applies to the second call to
672 // ProcessClauseToSimplifyOthersUsingLiteral() above too.
673 if ((clause_signature & ~ci_signature) == 0 &&
674 SimplifyClause(clause, &clauses_[ci], &opposite_literal,
675 &num_inspected_literals_)) {
676 DCHECK_EQ(opposite_literal, lit.NegatedIndex());
677 if (clauses_[ci].empty()) return false; // UNSAT.
678 if (drat_proof_handler_ != nullptr) {
679 // TODO(user): remove the old clauses_[ci] afterwards.
680 drat_proof_handler_->AddClause(clauses_[ci]);
681 }
682
683 // Recompute signature.
684 signatures_[ci] = ComputeSignatureOfClauseVariables(ci);
685
686 if (!in_clause_to_process_[ci]) {
687 in_clause_to_process_[ci] = true;
688 clause_to_process_.push_back(ci);
689 }
690 something_removed = true;
691 continue;
692 }
693 occurrence_list_ref[new_index] = ci;
694 ++new_index;
695 }
696 occurrence_list_ref.resize(new_index);
697 literal_to_clause_sizes_[lit.NegatedIndex()] = new_index;
698 if (something_removed) {
699 UpdatePriorityQueue(Literal(lit.NegatedIndex()).Variable());
700 }
701 }
702 return true;
703}
704
705void SatPresolver::RemoveAndRegisterForPostsolveAllClauseContaining(Literal x) {
706 for (ClauseIndex i : literal_to_clauses_[x.Index()]) {
707 if (!clauses_[i].empty()) RemoveAndRegisterForPostsolve(i, x);
708 }
709 gtl::STLClearObject(&literal_to_clauses_[x.Index()]);
710 literal_to_clause_sizes_[x.Index()] = 0;
711}
712
714 const int s1 = literal_to_clause_sizes_[x.Index()];
715 const int s2 = literal_to_clause_sizes_[x.NegatedIndex()];
716
717 // Note that if s1 or s2 is equal to 0, this function will implicitly just
718 // fix the variable x.
719 if (s1 == 0 && s2 == 0) return false;
720
721 // Heuristic. Abort if the work required to decide if x should be removed
722 // seems to big.
723 if (s1 > 1 && s2 > 1 && s1 * s2 > parameters_.presolve_bve_threshold()) {
724 return false;
725 }
726
727 // Compute the threshold under which we don't remove x.Variable().
728 int threshold = 0;
729 const int clause_weight = parameters_.presolve_bve_clause_weight();
730 for (ClauseIndex i : literal_to_clauses_[x.Index()]) {
731 if (!clauses_[i].empty()) {
732 threshold += clause_weight + clauses_[i].size();
733 }
734 }
735 for (ClauseIndex i : literal_to_clauses_[x.NegatedIndex()]) {
736 if (!clauses_[i].empty()) {
737 threshold += clause_weight + clauses_[i].size();
738 }
739 }
740
741 // For the BCE, we prefer s2 to be small.
742 if (s1 < s2) x = x.Negated();
743
744 // Test whether we should remove the x.Variable().
745 int size = 0;
746 for (ClauseIndex i : literal_to_clauses_[x.Index()]) {
747 if (clauses_[i].empty()) continue;
748 bool no_resolvant = true;
749 for (ClauseIndex j : literal_to_clauses_[x.NegatedIndex()]) {
750 if (clauses_[j].empty()) continue;
751 const int rs = ComputeResolvantSize(x, clauses_[i], clauses_[j]);
752 if (rs >= 0) {
753 no_resolvant = false;
754 size += clause_weight + rs;
755
756 // Abort early if the "size" become too big.
757 if (size > threshold) return false;
758 }
759 }
760 if (no_resolvant && parameters_.presolve_blocked_clause()) {
761 // This is an incomplete heuristic for blocked clause detection. Here,
762 // the clause i is "blocked", so we can remove it. Note that the code
763 // below already do that if we decide to eliminate x.
764 //
765 // For more details, see the paper "Blocked clause elimination", Matti
766 // Jarvisalo, Armin Biere, Marijn Heule. TACAS, volume 6015 of Lecture
767 // Notes in Computer Science, pages 129–144. Springer, 2010.
768 //
769 // TODO(user): Choose if we use x or x.Negated() depending on the list
770 // sizes? The function achieve the same if x = x.Negated(), however the
771 // loops are not done in the same order which may change this incomplete
772 // "blocked" clause detection.
773 RemoveAndRegisterForPostsolve(i, x);
774 }
775 }
776
777 // Add all the resolvant clauses.
778 // Note that the variable priority queue will only be updated during the
779 // deletion.
780 std::vector<Literal> temp;
781 for (ClauseIndex i : literal_to_clauses_[x.Index()]) {
782 if (clauses_[i].empty()) continue;
783 for (ClauseIndex j : literal_to_clauses_[x.NegatedIndex()]) {
784 if (clauses_[j].empty()) continue;
785 if (ComputeResolvant(x, clauses_[i], clauses_[j], &temp)) {
786 AddClauseInternal(&temp);
787 }
788 }
789 }
790
791 // Deletes the old clauses.
792 //
793 // TODO(user): We could only update the priority queue once for each variable
794 // instead of doing it many times.
795 RemoveAndRegisterForPostsolveAllClauseContaining(x);
796 RemoveAndRegisterForPostsolveAllClauseContaining(x.Negated());
797
798 // TODO(user): At this point x.Variable() is added back to the priority queue.
799 // Avoid doing that.
800 return true;
801}
802
803void SatPresolver::Remove(ClauseIndex ci) {
804 signatures_[ci] = 0;
805 for (Literal e : clauses_[ci]) {
806 literal_to_clause_sizes_[e.Index()]--;
807 UpdatePriorityQueue(e.Variable());
808 UpdateBvaPriorityQueue(Literal(e.Variable(), true).Index());
809 UpdateBvaPriorityQueue(Literal(e.Variable(), false).Index());
810 }
811 if (drat_proof_handler_ != nullptr) {
812 drat_proof_handler_->DeleteClause(clauses_[ci]);
813 }
814 gtl::STLClearObject(&clauses_[ci]);
815}
816
817void SatPresolver::RemoveAndRegisterForPostsolve(ClauseIndex ci, Literal x) {
818 postsolver_->Add(x, clauses_[ci]);
819 Remove(ci);
820}
821
822Literal SatPresolver::FindLiteralWithShortestOccurrenceList(
823 const std::vector<Literal>& clause) {
824 DCHECK(!clause.empty());
825 Literal result = clause.front();
826 int best_size = literal_to_clause_sizes_[result.Index()];
827 for (const Literal l : clause) {
828 const int size = literal_to_clause_sizes_[l.Index()];
829 if (size < best_size) {
830 result = l;
831 best_size = size;
832 }
833 }
834 return result;
835}
836
837LiteralIndex SatPresolver::FindLiteralWithShortestOccurrenceListExcluding(
838 const std::vector<Literal>& clause, Literal to_exclude) {
839 DCHECK(!clause.empty());
840 LiteralIndex result = kNoLiteralIndex;
841 int num_occurrences = std::numeric_limits<int>::max();
842 for (const Literal l : clause) {
843 if (l == to_exclude) continue;
844 if (literal_to_clause_sizes_[l.Index()] < num_occurrences) {
845 result = l.Index();
846 num_occurrences = literal_to_clause_sizes_[l.Index()];
847 }
848 }
849 return result;
850}
851
852void SatPresolver::UpdatePriorityQueue(BooleanVariable var) {
853 if (var_pq_elements_.empty()) return; // not initialized.
854 PQElement* element = &var_pq_elements_[var];
855 element->weight = literal_to_clause_sizes_[Literal(var, true).Index()] +
856 literal_to_clause_sizes_[Literal(var, false).Index()];
857 if (var_pq_.Contains(element)) {
858 var_pq_.NoteChangedPriority(element);
859 } else {
860 var_pq_.Add(element);
861 }
862}
863
864void SatPresolver::InitializePriorityQueue() {
865 const int num_vars = NumVariables();
866 var_pq_elements_.resize(num_vars);
867 for (BooleanVariable var(0); var < num_vars; ++var) {
868 PQElement* element = &var_pq_elements_[var];
869 element->variable = var;
870 element->weight = literal_to_clause_sizes_[Literal(var, true).Index()] +
871 literal_to_clause_sizes_[Literal(var, false).Index()];
872 var_pq_.Add(element);
873 }
874}
875
876void SatPresolver::UpdateBvaPriorityQueue(LiteralIndex lit) {
877 if (bva_pq_elements_.empty()) return; // not initialized.
878 DCHECK_LT(lit, bva_pq_elements_.size());
879 BvaPqElement* element = &bva_pq_elements_[lit.value()];
880 element->weight = literal_to_clause_sizes_[lit];
881 if (bva_pq_.Contains(element)) {
882 bva_pq_.NoteChangedPriority(element);
883 }
884}
885
886void SatPresolver::AddToBvaPriorityQueue(LiteralIndex lit) {
887 if (bva_pq_elements_.empty()) return; // not initialized.
888 DCHECK_LT(lit, bva_pq_elements_.size());
889 BvaPqElement* element = &bva_pq_elements_[lit.value()];
890 element->weight = literal_to_clause_sizes_[lit];
891 DCHECK(!bva_pq_.Contains(element));
892 if (element->weight > 2) bva_pq_.Add(element);
893}
894
895void SatPresolver::InitializeBvaPriorityQueue() {
896 const int num_literals = 2 * NumVariables();
897 bva_pq_.Clear();
898 bva_pq_elements_.assign(num_literals, BvaPqElement());
899 for (LiteralIndex lit(0); lit < num_literals; ++lit) {
900 BvaPqElement* element = &bva_pq_elements_[lit.value()];
901 element->literal = lit;
902 element->weight = literal_to_clause_sizes_[lit];
903
904 // If a literal occur only in two clauses, then there is no point calling
905 // SimpleBva() on it.
906 if (element->weight > 2) bva_pq_.Add(element);
907 }
908}
909
910void SatPresolver::DisplayStats(double elapsed_seconds) {
911 int num_literals = 0;
912 int num_clauses = 0;
913 int num_singleton_clauses = 0;
914 for (const std::vector<Literal>& c : clauses_) {
915 if (!c.empty()) {
916 if (c.size() == 1) ++num_singleton_clauses;
917 ++num_clauses;
918 num_literals += c.size();
919 }
920 }
921 int num_one_side = 0;
922 int num_simple_definition = 0;
923 int num_vars = 0;
924 for (BooleanVariable var(0); var < NumVariables(); ++var) {
925 const int s1 = literal_to_clause_sizes_[Literal(var, true).Index()];
926 const int s2 = literal_to_clause_sizes_[Literal(var, false).Index()];
927 if (s1 == 0 && s2 == 0) continue;
928
929 ++num_vars;
930 if (s1 == 0 || s2 == 0) {
931 num_one_side++;
932 } else if (s1 == 1 || s2 == 1) {
933 num_simple_definition++;
934 }
935 }
936 SOLVER_LOG(logger_, "[SAT presolve] [", elapsed_seconds, "s]",
937 " clauses:", num_clauses, " literals:", num_literals,
938 " vars:", num_vars, " one_side_vars:", num_one_side,
939 " simple_definition:", num_simple_definition,
940 " singleton_clauses:", num_singleton_clauses);
941}
942
943bool SimplifyClause(const std::vector<Literal>& a, std::vector<Literal>* b,
944 LiteralIndex* opposite_literal,
945 int64_t* num_inspected_literals) {
946 if (b->size() < a.size()) return false;
947 DCHECK(std::is_sorted(a.begin(), a.end()));
948 DCHECK(std::is_sorted(b->begin(), b->end()));
949 if (num_inspected_literals != nullptr) {
950 *num_inspected_literals += a.size();
951 *num_inspected_literals += b->size();
952 }
953
954 *opposite_literal = LiteralIndex(-1);
955
956 int num_diff = 0;
957 std::vector<Literal>::const_iterator ia = a.begin();
958 std::vector<Literal>::const_iterator ib = b->begin();
959 std::vector<Literal>::const_iterator to_remove;
960
961 // Because we abort early when size_diff becomes negative, the second test
962 // in the while loop is not needed.
963 int size_diff = b->size() - a.size();
964 while (ia != a.end() /* && ib != b->end() */) {
965 if (*ia == *ib) { // Same literal.
966 ++ia;
967 ++ib;
968 } else if (*ia == ib->Negated()) { // Opposite literal.
969 ++num_diff;
970 if (num_diff > 1) return false; // Too much difference.
971 to_remove = ib;
972 ++ia;
973 ++ib;
974 } else if (*ia < *ib) {
975 return false; // A literal of a is not in b.
976 } else { // *ia > *ib
977 ++ib;
978
979 // A literal of b is not in a, we can abort early by comparing the sizes
980 // left.
981 if (--size_diff < 0) return false;
982 }
983 }
984 if (num_diff == 1) {
985 *opposite_literal = to_remove->Index();
986 b->erase(to_remove);
987 }
988 return true;
989}
990
991LiteralIndex DifferAtGivenLiteral(const std::vector<Literal>& a,
992 const std::vector<Literal>& b, Literal l) {
993 DCHECK_EQ(b.size(), a.size());
994 DCHECK(std::is_sorted(a.begin(), a.end()));
995 DCHECK(std::is_sorted(b.begin(), b.end()));
996 LiteralIndex result = kNoLiteralIndex;
997 std::vector<Literal>::const_iterator ia = a.begin();
998 std::vector<Literal>::const_iterator ib = b.begin();
999 while (ia != a.end() && ib != b.end()) {
1000 if (*ia == *ib) { // Same literal.
1001 ++ia;
1002 ++ib;
1003 } else if (*ia < *ib) {
1004 // A literal of a is not in b, it must be l.
1005 // Note that this can only happen once.
1006 if (*ia != l) return kNoLiteralIndex;
1007 ++ia;
1008 } else {
1009 // A literal of b is not in a, save it.
1010 // We abort if this happen twice.
1011 if (result != kNoLiteralIndex) return kNoLiteralIndex;
1012 result = (*ib).Index();
1013 ++ib;
1014 }
1015 }
1016 // Check the corner case of the difference at the end.
1017 if (ia != a.end() && *ia != l) return kNoLiteralIndex;
1018 if (ib != b.end()) {
1019 if (result != kNoLiteralIndex) return kNoLiteralIndex;
1020 result = (*ib).Index();
1021 }
1022 return result;
1023}
1024
1025bool ComputeResolvant(Literal x, const std::vector<Literal>& a,
1026 const std::vector<Literal>& b,
1027 std::vector<Literal>* out) {
1028 DCHECK(std::is_sorted(a.begin(), a.end()));
1029 DCHECK(std::is_sorted(b.begin(), b.end()));
1030
1031 out->clear();
1032 std::vector<Literal>::const_iterator ia = a.begin();
1033 std::vector<Literal>::const_iterator ib = b.begin();
1034 while ((ia != a.end()) && (ib != b.end())) {
1035 if (*ia == *ib) {
1036 out->push_back(*ia);
1037 ++ia;
1038 ++ib;
1039 } else if (*ia == ib->Negated()) {
1040 if (*ia != x) return false; // Trivially true.
1041 DCHECK_EQ(*ib, x.Negated());
1042 ++ia;
1043 ++ib;
1044 } else if (*ia < *ib) {
1045 out->push_back(*ia);
1046 ++ia;
1047 } else { // *ia > *ib
1048 out->push_back(*ib);
1049 ++ib;
1050 }
1051 }
1052
1053 // Copy remaining literals.
1054 out->insert(out->end(), ia, a.end());
1055 out->insert(out->end(), ib, b.end());
1056 return true;
1057}
1058
1059// Note that this function takes a big chunk of the presolve running time.
1060int ComputeResolvantSize(Literal x, const std::vector<Literal>& a,
1061 const std::vector<Literal>& b) {
1062 DCHECK(std::is_sorted(a.begin(), a.end()));
1063 DCHECK(std::is_sorted(b.begin(), b.end()));
1064
1065 int size = static_cast<int>(a.size() + b.size()) - 2;
1066 std::vector<Literal>::const_iterator ia = a.begin();
1067 std::vector<Literal>::const_iterator ib = b.begin();
1068 while ((ia != a.end()) && (ib != b.end())) {
1069 if (*ia == *ib) {
1070 --size;
1071 ++ia;
1072 ++ib;
1073 } else if (*ia == ib->Negated()) {
1074 if (*ia != x) return -1; // Trivially true.
1075 DCHECK_EQ(*ib, x.Negated());
1076 ++ia;
1077 ++ib;
1078 } else if (*ia < *ib) {
1079 ++ia;
1080 } else { // *ia > *ib
1081 ++ib;
1082 }
1083 }
1084 DCHECK_GE(size, 0);
1085 return size;
1086}
1087
1088// A simple graph where the nodes are the literals and the nodes adjacent to a
1089// literal l are the propagated literal when l is assigned in the underlying
1090// sat solver.
1091//
1092// This can be used to do a strong component analysis while probing all the
1093// literals of a solver. Note that this can be expensive, hence the support
1094// for a deterministic time limit.
1096 public:
1097 PropagationGraph(double deterministic_time_limit, SatSolver* solver)
1098 : solver_(solver),
1099 deterministic_time_limit(solver->deterministic_time() +
1100 deterministic_time_limit) {}
1101
1102 // Returns the set of node adjacent to the given one.
1103 // Interface needed by FindStronglyConnectedComponents(), note that it needs
1104 // to be const.
1105 const std::vector<int32_t>& operator[](int32_t index) const {
1106 scratchpad_.clear();
1107 solver_->Backtrack(0);
1108
1109 // Note that when the time limit is reached, we just keep returning empty
1110 // adjacency list. This way, the SCC algorithm will terminate quickly and
1111 // the equivalent literals detection will be incomplete but correct. Note
1112 // also that thanks to the SCC algorithm, we will explore the connected
1113 // components first.
1114 if (solver_->deterministic_time() > deterministic_time_limit) {
1115 return scratchpad_;
1116 }
1117
1118 const Literal l = Literal(LiteralIndex(index));
1119 if (!solver_->Assignment().LiteralIsAssigned(l)) {
1120 const int trail_index = solver_->LiteralTrail().Index();
1122 if (solver_->CurrentDecisionLevel() > 0) {
1123 // Note that the +1 is to avoid adding a => a.
1124 for (int i = trail_index + 1; i < solver_->LiteralTrail().Index();
1125 ++i) {
1126 scratchpad_.push_back(solver_->LiteralTrail()[i].Index().value());
1127 }
1128 }
1129 }
1130 return scratchpad_;
1131 }
1132
1133 private:
1134 mutable std::vector<int32_t> scratchpad_;
1135 SatSolver* const solver_;
1136 const double deterministic_time_limit;
1137
1138 DISALLOW_COPY_AND_ASSIGN(PropagationGraph);
1139};
1140
1142 SatSolver* solver, SatPostsolver* postsolver,
1143 DratProofHandler* drat_proof_handler,
1145 WallTimer timer;
1146 timer.Start();
1147
1148 solver->Backtrack(0);
1149 mapping->clear();
1150 const int num_already_fixed_vars = solver->LiteralTrail().Index();
1151
1152 PropagationGraph graph(
1153 solver->parameters().presolve_probing_deterministic_time_limit(), solver);
1154 const int32_t size = solver->NumVariables() * 2;
1155 std::vector<std::vector<int32_t>> scc;
1156 FindStronglyConnectedComponents(size, graph, &scc);
1157
1158 // We have no guarantee that the cycle of x and not(x) touch the same
1159 // variables. This is because we may have more info for the literal probed
1160 // later or the propagation may go only in one direction. For instance if we
1161 // have two clauses (not(x1) v x2) and (not(x1) v not(x2) v x3) then x1
1162 // implies x2 and x3 but not(x3) doesn't imply anything by unit propagation.
1163 //
1164 // TODO(user): Add some constraint so that it does?
1165 //
1166 // Because of this, we "merge" the cycles.
1167 MergingPartition partition(size);
1168 for (const std::vector<int32_t>& component : scc) {
1169 if (component.size() > 1) {
1170 if (mapping->empty()) mapping->resize(size, LiteralIndex(-1));
1171 const Literal representative((LiteralIndex(component[0])));
1172 for (int i = 1; i < component.size(); ++i) {
1173 const Literal l((LiteralIndex(component[i])));
1174 // TODO(user): check compatibility? if x ~ not(x) => unsat.
1175 // but probably, the solver would have found this too? not sure...
1176 partition.MergePartsOf(representative.Index().value(),
1177 l.Index().value());
1178 partition.MergePartsOf(representative.NegatedIndex().value(),
1179 l.NegatedIndex().value());
1180 }
1181
1182 // We rely on the fact that the representative of a literal x and the one
1183 // of its negation are the same variable.
1184 DCHECK_EQ(Literal(LiteralIndex(partition.GetRootAndCompressPath(
1185 representative.Index().value()))),
1186 Literal(LiteralIndex(partition.GetRootAndCompressPath(
1187 representative.NegatedIndex().value())))
1188 .Negated());
1189 }
1190 }
1191
1192 solver->Backtrack(0);
1193 int num_equiv = 0;
1194 if (!mapping->empty()) {
1195 // If a variable in a cycle is fixed. We want to fix all of them.
1196 //
1197 // We first fix all representative if one variable of the cycle is fixed. In
1198 // a second pass we fix all the variable of a cycle whose representative is
1199 // fixed.
1200 //
1201 // TODO(user): Fixing a variable might fix more of them by propagation, so
1202 // we might not fix everything possible with these loops.
1203 const VariablesAssignment& assignment = solver->Assignment();
1204 for (LiteralIndex i(0); i < size; ++i) {
1205 const LiteralIndex rep(partition.GetRootAndCompressPath(i.value()));
1206 if (assignment.LiteralIsAssigned(Literal(i)) &&
1207 !assignment.LiteralIsAssigned(Literal(rep))) {
1208 const Literal true_lit = assignment.LiteralIsTrue(Literal(i))
1209 ? Literal(rep)
1210 : Literal(rep).Negated();
1211 solver->AddUnitClause(true_lit);
1212 if (drat_proof_handler != nullptr) {
1213 drat_proof_handler->AddClause({true_lit});
1214 }
1215 }
1216 }
1217 for (LiteralIndex i(0); i < size; ++i) {
1218 const LiteralIndex rep(partition.GetRootAndCompressPath(i.value()));
1219 (*mapping)[i] = rep;
1220 if (assignment.LiteralIsAssigned(Literal(rep))) {
1221 if (!assignment.LiteralIsAssigned(Literal(i))) {
1222 const Literal true_lit = assignment.LiteralIsTrue(Literal(rep))
1223 ? Literal(i)
1224 : Literal(i).Negated();
1225 solver->AddUnitClause(true_lit);
1226 if (drat_proof_handler != nullptr) {
1227 drat_proof_handler->AddClause({true_lit});
1228 }
1229 }
1230 } else if (assignment.LiteralIsAssigned(Literal(i))) {
1231 if (!assignment.LiteralIsAssigned(Literal(rep))) {
1232 const Literal true_lit = assignment.LiteralIsTrue(Literal(i))
1233 ? Literal(rep)
1234 : Literal(rep).Negated();
1235 solver->AddUnitClause(true_lit);
1236 if (drat_proof_handler != nullptr) {
1237 drat_proof_handler->AddClause({true_lit});
1238 }
1239 }
1240 } else if (rep != i) {
1241 ++num_equiv;
1242 postsolver->Add(Literal(i), {Literal(i), Literal(rep).Negated()});
1243 if (drat_proof_handler != nullptr) {
1244 drat_proof_handler->AddClause({Literal(i), Literal(rep).Negated()});
1245 }
1246 }
1247 }
1248 }
1249
1250 const bool log_info =
1251 solver->parameters().log_search_progress() || VLOG_IS_ON(1);
1252 LOG_IF(INFO, log_info) << "Probing. fixed " << num_already_fixed_vars << " + "
1253 << solver->LiteralTrail().Index() -
1254 num_already_fixed_vars
1255 << " equiv " << num_equiv / 2 << " total "
1256 << solver->NumVariables() << " wtime: " << timer.Get();
1257}
1258
1259SatSolver::Status SolveWithPresolve(std::unique_ptr<SatSolver>* solver,
1261 std::vector<bool>* solution,
1262 DratProofHandler* drat_proof_handler,
1263 SolverLogger* logger) {
1264 // We save the initial parameters.
1265 const SatParameters parameters = (*solver)->parameters();
1266 SatPostsolver postsolver((*solver)->NumVariables());
1267
1268 const bool log_info = parameters.log_search_progress() || VLOG_IS_ON(1);
1269
1270 // Some problems are formulated in such a way that our SAT heuristics
1271 // simply works without conflict. Get them out of the way first because it
1272 // is possible that the presolve lose this "lucky" ordering. This is in
1273 // particular the case on the SAT14.crafted.complete-xxx-... problems.
1274 {
1275 Model* model = (*solver)->model();
1276 const double dtime = std::min(1.0, time_limit->GetDeterministicTimeLeft());
1277 if (!LookForTrivialSatSolution(dtime, model)) {
1278 VLOG(1) << "UNSAT during probing.";
1279 return SatSolver::INFEASIBLE;
1280 }
1281 const int num_variables = (*solver)->NumVariables();
1282 if ((*solver)->LiteralTrail().Index() == num_variables) {
1283 VLOG(1) << "Problem solved by trivial heuristic!";
1284 solution->clear();
1285 for (int i = 0; i < (*solver)->NumVariables(); ++i) {
1286 solution->push_back((*solver)->Assignment().LiteralIsTrue(
1287 Literal(BooleanVariable(i), true)));
1288 }
1289 return SatSolver::FEASIBLE;
1290 }
1291 }
1292
1293 // We use a new block so the memory used by the presolver can be
1294 // reclaimed as soon as it is no longer needed.
1295 const int max_num_passes = 4;
1296 for (int i = 0; i < max_num_passes && !time_limit->LimitReached(); ++i) {
1297 const int saved_num_variables = (*solver)->NumVariables();
1298
1299 // Run the new preprocessing code. Note that the probing that it does is
1300 // faster than the ProbeAndFindEquivalentLiteral() call below, but does not
1301 // do equivalence detection as completely, so we still apply the other
1302 // "probing" code afterwards even if it will not fix more literals, but it
1303 // will do one pass of proper equivalence detection.
1304 {
1305 Model* model = (*solver)->model();
1306 model->GetOrCreate<TimeLimit>()->MergeWithGlobalTimeLimit(time_limit);
1307 SatPresolveOptions options;
1308 options.log_info = log_info;
1309 options.extract_binary_clauses_in_probing = false;
1310 options.use_transitive_reduction = false;
1311 options.deterministic_time_limit =
1312 parameters.presolve_probing_deterministic_time_limit();
1313
1314 if (!model->GetOrCreate<Inprocessing>()->PresolveLoop(options)) {
1315 VLOG(1) << "UNSAT during probing.";
1316 return SatSolver::INFEASIBLE;
1317 }
1318 for (const auto& c : model->GetOrCreate<PostsolveClauses>()->clauses) {
1319 postsolver.Add(c[0], c);
1320 }
1321 }
1322
1323 // Probe + find equivalent literals.
1324 // TODO(user): Use a derived time limit in the probing phase.
1326 ProbeAndFindEquivalentLiteral((*solver).get(), &postsolver,
1327 drat_proof_handler, &equiv_map);
1328 if ((*solver)->IsModelUnsat()) {
1329 VLOG(1) << "UNSAT during probing.";
1330 return SatSolver::INFEASIBLE;
1331 }
1332
1333 // The rest of the presolve only work on pure SAT problem.
1334 if (!(*solver)->ProblemIsPureSat()) {
1335 VLOG(1) << "The problem is not a pure SAT problem, skipping the SAT "
1336 "specific presolve.";
1337 break;
1338 }
1339
1340 // Register the fixed variables with the postsolver.
1341 // TODO(user): Find a better place for this?
1342 (*solver)->Backtrack(0);
1343 for (int i = 0; i < (*solver)->LiteralTrail().Index(); ++i) {
1344 postsolver.FixVariable((*solver)->LiteralTrail()[i]);
1345 }
1346
1347 // TODO(user): Pass the time_limit to the presolver.
1348 SatPresolver presolver(&postsolver, logger);
1349 presolver.SetParameters(parameters);
1350 presolver.SetDratProofHandler(drat_proof_handler);
1351 presolver.SetEquivalentLiteralMapping(equiv_map);
1352 (*solver)->ExtractClauses(&presolver);
1353 (*solver)->AdvanceDeterministicTime(time_limit);
1354
1355 // Tricky: the model local time limit is updated by the new functions, but
1356 // the old ones update time_limit directly.
1358 ->model()
1359 ->GetOrCreate<TimeLimit>()
1360 ->GetElapsedDeterministicTime());
1361
1362 (*solver).reset(nullptr);
1363 std::vector<bool> can_be_removed(presolver.NumVariables(), true);
1364 if (!presolver.Presolve(can_be_removed)) {
1365 VLOG(1) << "UNSAT during presolve.";
1366
1367 // This is just here to reset the SatSolver::Solve() statistics.
1368 (*solver) = absl::make_unique<SatSolver>();
1369 return SatSolver::INFEASIBLE;
1370 }
1371
1372 postsolver.ApplyMapping(presolver.VariableMapping());
1373 if (drat_proof_handler != nullptr) {
1374 drat_proof_handler->ApplyMapping(presolver.VariableMapping());
1375 }
1376
1377 // Load the presolved problem in a new solver.
1378 (*solver) = absl::make_unique<SatSolver>();
1379 (*solver)->SetDratProofHandler(drat_proof_handler);
1380 (*solver)->SetParameters(parameters);
1381 presolver.LoadProblemIntoSatSolver((*solver).get());
1382
1383 // Stop if a fixed point has been reached.
1384 if ((*solver)->NumVariables() == saved_num_variables) break;
1385 }
1386
1387 // Before solving, we use the new probing code that adds all new binary
1388 // implication it can find to the binary implication graph. This gives good
1389 // benefits. Note that we currently do not do it before presolve because then
1390 // the current presolve code does not work too well with the potential huge
1391 // number of binary clauses added.
1392 //
1393 // TODO(user): Revisit the situation when we simplify better all the clauses
1394 // using binary ones. Or if/when we support at most one better in pure SAT
1395 // solving and presolve.
1396 {
1397 Model* model = (*solver)->model();
1398 model->GetOrCreate<TimeLimit>()->MergeWithGlobalTimeLimit(time_limit);
1399 SatPresolveOptions options;
1400 options.log_info = log_info;
1401 options.use_transitive_reduction = true;
1402 options.extract_binary_clauses_in_probing = true;
1403 options.deterministic_time_limit =
1404 model->GetOrCreate<SatParameters>()
1405 ->presolve_probing_deterministic_time_limit();
1406 if (!model->GetOrCreate<Inprocessing>()->PresolveLoop(options)) {
1407 return SatSolver::INFEASIBLE;
1408 }
1409 for (const auto& c : model->GetOrCreate<PostsolveClauses>()->clauses) {
1410 postsolver.Add(c[0], c);
1411 }
1412 }
1413
1414 // Solve.
1415 const SatSolver::Status result = (*solver)->SolveWithTimeLimit(time_limit);
1416 if (result == SatSolver::FEASIBLE) {
1417 *solution = postsolver.ExtractAndPostsolveSolution(**solver);
1418 }
1419 return result;
1420}
1421
1422} // namespace sat
1423} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define LOG_IF(severity, condition)
Definition: base/logging.h:479
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:892
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:896
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:894
#define DCHECK(condition)
Definition: base/logging.h:890
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:891
#define VLOG(verboselevel)
Definition: base/logging.h:984
bool Contains(const T *val) const
void Start()
Definition: timer.h:31
double Get() const
Definition: timer.h:45
void resize(size_type new_size)
size_type size() const
bool empty() const
void push_back(const value_type &x)
int MergePartsOf(int node1, int node2)
void AdvanceDeterministicTime(double deterministic_duration)
Definition: time_limit.h:387
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
Definition: time_limit.h:546
void DeleteClause(absl::Span< const Literal > clause)
void ApplyMapping(const absl::StrongVector< BooleanVariable, BooleanVariable > &mapping)
void AddClause(absl::Span< const Literal > clause)
bool PresolveLoop(SatPresolveOptions options)
LiteralIndex NegatedIndex() const
Definition: sat_base.h:88
LiteralIndex Index() const
Definition: sat_base.h:87
BooleanVariable Variable() const
Definition: sat_base.h:83
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
const std::vector< int32_t > & operator[](int32_t index) const
PropagationGraph(double deterministic_time_limit, SatSolver *solver)
void Add(Literal x, const absl::Span< const Literal > clause)
void ApplyMapping(const absl::StrongVector< BooleanVariable, BooleanVariable > &mapping)
std::vector< bool > PostsolveSolution(const std::vector< bool > &solution)
std::vector< bool > ExtractAndPostsolveSolution(const SatSolver &solver)
void SetNumVariables(int num_variables)
void LoadProblemIntoSatSolver(SatSolver *solver)
void AddBinaryClause(Literal a, Literal b)
void SetEquivalentLiteralMapping(const absl::StrongVector< LiteralIndex, LiteralIndex > &mapping)
void SetParameters(const SatParameters &params)
absl::StrongVector< BooleanVariable, BooleanVariable > VariableMapping() const
void SetDratProofHandler(DratProofHandler *drat_proof_handler)
void AddClause(absl::Span< const Literal > clause)
bool ProcessClauseToSimplifyOthers(ClauseIndex clause_index)
void SetNumVariables(int num_variables)
Definition: sat_solver.cc:85
const SatParameters & parameters() const
Definition: sat_solver.cc:131
const VariablesAssignment & Assignment() const
Definition: sat_solver.h:378
const Trail & LiteralTrail() const
Definition: sat_solver.h:377
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
Definition: sat_solver.cc:536
void Backtrack(int target_level)
Definition: sat_solver.cc:991
bool AddProblemClause(absl::Span< const Literal > literals)
Definition: sat_solver.cc:202
bool AddUnitClause(Literal true_literal)
Definition: sat_solver.cc:185
bool LiteralIsAssigned(Literal literal) const
Definition: sat_base.h:156
bool VariableIsAssigned(BooleanVariable var) const
Definition: sat_base.h:161
bool LiteralIsTrue(Literal literal) const
Definition: sat_base.h:153
void AssignFromTrueLiteral(Literal literal)
Definition: sat_base.h:136
int64_t b
int64_t a
SatParameters parameters
ModelSharedTimeLimit * time_limit
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
int index
const int INFO
Definition: log_severity.h:31
void STLEraseAllFromSequence(T *v, const E &e)
Definition: stl_util.h:93
void STLClearObject(T *obj)
Definition: stl_util.h:123
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:262
bool LookForTrivialSatSolution(double deterministic_time_limit, Model *model)
Definition: probing.cc:290
int ComputeResolvantSize(Literal x, const std::vector< Literal > &a, const std::vector< Literal > &b)
const LiteralIndex kNoLiteralIndex(-1)
LiteralIndex DifferAtGivenLiteral(const std::vector< Literal > &a, const std::vector< Literal > &b, Literal l)
bool SimplifyClause(const std::vector< Literal > &a, std::vector< Literal > *b, LiteralIndex *opposite_literal, int64_t *num_inspected_literals)
bool ComputeResolvant(Literal x, const std::vector< Literal > &a, const std::vector< Literal > &b, std::vector< Literal > *out)
SatSolver::Status SolveWithPresolve(std::unique_ptr< SatSolver > *solver, TimeLimit *time_limit, std::vector< bool > *solution, DratProofHandler *drat_proof_handler, SolverLogger *logger)
void ProbeAndFindEquivalentLiteral(SatSolver *solver, SatPostsolver *postsolver, DratProofHandler *drat_proof_handler, absl::StrongVector< LiteralIndex, LiteralIndex > *mapping)
const BooleanVariable kNoBooleanVariable(-1)
Collection of objects used to extend the Constraint Solver library.
ColIndex representative
void FindStronglyConnectedComponents(const NodeIndex num_nodes, const Graph &graph, SccOutput *components)
std::deque< std::vector< Literal > > clauses
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:69
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:44