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