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