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