OR-Tools  8.1
probing.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 
14 #include "ortools/sat/probing.h"
15 
16 #include <set>
17 
19 #include "ortools/base/timer.h"
20 #include "ortools/sat/clause.h"
22 #include "ortools/sat/integer.h"
23 #include "ortools/sat/sat_base.h"
24 #include "ortools/sat/sat_solver.h"
25 #include "ortools/sat/util.h"
27 
28 namespace operations_research {
29 namespace sat {
30 
32  : trail_(*model->GetOrCreate<Trail>()),
33  assignment_(model->GetOrCreate<SatSolver>()->Assignment()),
34  integer_trail_(model->GetOrCreate<IntegerTrail>()),
35  implied_bounds_(model->GetOrCreate<ImpliedBounds>()),
36  sat_solver_(model->GetOrCreate<SatSolver>()),
37  time_limit_(model->GetOrCreate<TimeLimit>()),
38  implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()) {}
39 
40 bool Prober::ProbeBooleanVariables(const double deterministic_time_limit,
41  bool log_info) {
42  const int num_variables = sat_solver_->NumVariables();
43  std::vector<BooleanVariable> bool_vars;
44  for (BooleanVariable b(0); b < num_variables; ++b) {
45  const Literal literal(b, true);
46  if (implication_graph_->RepresentativeOf(literal) != literal) {
47  continue;
48  }
49  bool_vars.push_back(b);
50  }
51  return ProbeBooleanVariables(deterministic_time_limit, bool_vars, log_info);
52 }
53 
54 bool Prober::ProbeOneVariableInternal(BooleanVariable b) {
55  new_integer_bounds_.clear();
56  propagated_.SparseClearAll();
57  for (const Literal decision : {Literal(b, true), Literal(b, false)}) {
58  if (assignment_.LiteralIsAssigned(decision)) continue;
59 
60  CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
61  const int saved_index = trail_.Index();
62  sat_solver_->EnqueueDecisionAndBackjumpOnConflict(decision);
63  sat_solver_->AdvanceDeterministicTime(time_limit_);
64 
65  if (sat_solver_->IsModelUnsat()) return false;
66  if (sat_solver_->CurrentDecisionLevel() == 0) continue;
67 
68  implied_bounds_->ProcessIntegerTrail(decision);
69  integer_trail_->AppendNewBounds(&new_integer_bounds_);
70  for (int i = saved_index + 1; i < trail_.Index(); ++i) {
71  const Literal l = trail_[i];
72 
73  // We mark on the first run (b.IsPositive()) and check on the second.
74  if (decision.IsPositive()) {
75  propagated_.Set(l.Index());
76  } else {
77  if (propagated_[l.Index()]) {
78  to_fix_at_true_.push_back(l);
79  }
80  }
81 
82  // Anything not propagated by the BinaryImplicationGraph is a "new"
83  // binary clause. This is becaue the BinaryImplicationGraph has the
84  // highest priority of all propagators.
85  if (trail_.AssignmentType(l.Variable()) !=
86  implication_graph_->PropagatorId()) {
87  new_binary_clauses_.push_back({decision.Negated(), l});
88  }
89  }
90 
91  // Fix variable and add new binary clauses.
92  if (!sat_solver_->RestoreSolverToAssumptionLevel()) return false;
93  for (const Literal l : to_fix_at_true_) {
94  sat_solver_->AddUnitClause(l);
95  }
96  to_fix_at_true_.clear();
97  if (!sat_solver_->FinishPropagation()) return false;
98  num_new_binary_ += new_binary_clauses_.size();
99  for (auto binary : new_binary_clauses_) {
100  sat_solver_->AddBinaryClause(binary.first, binary.second);
101  }
102  new_binary_clauses_.clear();
103  if (!sat_solver_->FinishPropagation()) return false;
104  }
105 
106  // We have at most two lower bounds for each variables (one for b==0 and one
107  // for b==1), so the min of the two is a valid level zero bound! More
108  // generally, the domain of a variable can be intersected with the union
109  // of the two propagated domains. This also allow to detect "holes".
110  //
111  // TODO(user): More generally, for any clauses (b or not(b) is one), we
112  // could probe all the literal inside, and for any integer variable, we can
113  // take the union of the propagated domain as a new domain.
114  //
115  // TODO(user): fix binary variable in the same way? It might not be as
116  // useful since probing on such variable will also fix it. But then we might
117  // abort probing early, so it might still be good.
118  std::sort(new_integer_bounds_.begin(), new_integer_bounds_.end(),
119  [](IntegerLiteral a, IntegerLiteral b) { return a.var < b.var; });
120 
121  // This is used for the hole detection.
122  IntegerVariable prev_var = kNoIntegerVariable;
123  IntegerValue lb_max = kMinIntegerValue;
124  IntegerValue ub_min = kMaxIntegerValue;
125  new_integer_bounds_.push_back(IntegerLiteral()); // Sentinel.
126 
127  for (int i = 0; i < new_integer_bounds_.size(); ++i) {
128  const IntegerVariable var = new_integer_bounds_[i].var;
129 
130  // Hole detection.
131  if (i > 0 && PositiveVariable(var) != prev_var) {
132  if (ub_min + 1 < lb_max) {
133  // The variable cannot take value in (ub_min, lb_max) !
134  //
135  // TODO(user): do not create domain with a complexity that is too
136  // large?
137  const Domain old_domain =
138  integer_trail_->InitialVariableDomain(prev_var);
139  const Domain new_domain = old_domain.IntersectionWith(
140  Domain(ub_min.value() + 1, lb_max.value() - 1).Complement());
141  if (new_domain != old_domain) {
142  ++num_new_holes_;
143  if (!integer_trail_->UpdateInitialDomain(prev_var, new_domain)) {
144  return false;
145  }
146  }
147  }
148 
149  // Reinitialize.
150  lb_max = kMinIntegerValue;
151  ub_min = kMaxIntegerValue;
152  }
153 
154  prev_var = PositiveVariable(var);
155  if (VariableIsPositive(var)) {
156  lb_max = std::max(lb_max, new_integer_bounds_[i].bound);
157  } else {
158  ub_min = std::min(ub_min, -new_integer_bounds_[i].bound);
159  }
160 
161  // Bound tightening.
162  if (i == 0 || new_integer_bounds_[i - 1].var != var) continue;
163  const IntegerValue new_bound = std::min(new_integer_bounds_[i - 1].bound,
164  new_integer_bounds_[i].bound);
165  if (new_bound > integer_trail_->LowerBound(var)) {
166  ++num_new_integer_bounds_;
167  if (!integer_trail_->Enqueue(
168  IntegerLiteral::GreaterOrEqual(var, new_bound), {}, {})) {
169  return false;
170  }
171  }
172  }
173 
174  // We might have updated some integer domain, let's propagate.
175  return sat_solver_->FinishPropagation();
176 }
177 
178 bool Prober::ProbeOneVariable(BooleanVariable b) {
179  // Reset statistics.
180  num_new_binary_ = 0;
181  num_new_holes_ = 0;
182  num_new_integer_bounds_ = 0;
183 
184  // Resize the propagated sparse bitset.
185  const int num_variables = sat_solver_->NumVariables();
186  propagated_.ClearAndResize(LiteralIndex(2 * num_variables));
187 
188  // Reset the solver in case it was already used.
189  sat_solver_->SetAssumptionLevel(0);
190  if (!sat_solver_->RestoreSolverToAssumptionLevel()) return false;
191 
192  return ProbeOneVariableInternal(b);
193 }
194 
195 bool Prober::ProbeBooleanVariables(const double deterministic_time_limit,
196  absl::Span<const BooleanVariable> bool_vars,
197  bool log_info) {
198  log_info |= VLOG_IS_ON(1);
200  wall_timer.Start();
201 
202  // Reset statistics.
203  num_new_binary_ = 0;
204  num_new_holes_ = 0;
205  num_new_integer_bounds_ = 0;
206 
207  // Resize the propagated sparse bitset.
208  const int num_variables = sat_solver_->NumVariables();
209  propagated_.ClearAndResize(LiteralIndex(2 * num_variables));
210 
211  // Reset the solver in case it was already used.
212  sat_solver_->SetAssumptionLevel(0);
213  if (!sat_solver_->RestoreSolverToAssumptionLevel()) return false;
214 
215  const int initial_num_fixed = sat_solver_->LiteralTrail().Index();
216  const double initial_deterministic_time =
217  time_limit_->GetElapsedDeterministicTime();
218  const double limit = initial_deterministic_time + deterministic_time_limit;
219 
220  bool limit_reached = false;
221  int num_probed = 0;
222 
223  for (const BooleanVariable b : bool_vars) {
224  const Literal literal(b, true);
225  if (implication_graph_->RepresentativeOf(literal) != literal) {
226  continue;
227  }
228 
229  // TODO(user): Instead of an hard deterministic limit, we should probably
230  // use a lower one, but reset it each time we have found something useful.
231  if (time_limit_->LimitReached() ||
232  time_limit_->GetElapsedDeterministicTime() > limit) {
233  limit_reached = true;
234  break;
235  }
236 
237  // Propagate b=1 and then b=0.
238  ++num_probed;
239  if (!ProbeOneVariableInternal(b)) {
240  return false;
241  }
242  }
243 
244  // Display stats.
245  if (log_info) {
246  const double time_diff =
247  time_limit_->GetElapsedDeterministicTime() - initial_deterministic_time;
248  const int num_fixed = sat_solver_->LiteralTrail().Index();
249  const int num_newly_fixed = num_fixed - initial_num_fixed;
250  LOG(INFO) << "Probing deterministic_time: " << time_diff
251  << " (limit: " << deterministic_time_limit
252  << ") wall_time: " << wall_timer.Get() << " ("
253  << (limit_reached ? "Aborted " : "") << num_probed << "/"
254  << bool_vars.size() << ")";
255  LOG_IF(INFO, num_newly_fixed > 0)
256  << " - new fixed Boolean: " << num_newly_fixed << " (" << num_fixed
257  << "/" << sat_solver_->NumVariables() << ")";
258  LOG_IF(INFO, num_new_holes_ > 0)
259  << " - new integer holes: " << num_new_holes_;
260  LOG_IF(INFO, num_new_integer_bounds_ > 0)
261  << " - new integer bounds: " << num_new_integer_bounds_;
262  LOG_IF(INFO, num_new_binary_ > 0)
263  << " - new binary clause: " << num_new_binary_;
264  }
265 
266  return true;
267 }
268 
269 bool LookForTrivialSatSolution(double deterministic_time_limit, Model* model,
270  bool log_info) {
271  log_info |= VLOG_IS_ON(1);
273  wall_timer.Start();
274 
275  // Reset the solver in case it was already used.
276  auto* sat_solver = model->GetOrCreate<SatSolver>();
277  sat_solver->SetAssumptionLevel(0);
278  if (!sat_solver->RestoreSolverToAssumptionLevel()) return false;
279 
280  auto* time_limit = model->GetOrCreate<TimeLimit>();
281  const int initial_num_fixed = sat_solver->LiteralTrail().Index();
282 
283  // Note that this code do not care about the non-Boolean part and just try to
284  // assign the existing Booleans.
285  SatParameters initial_params = *model->GetOrCreate<SatParameters>();
286  SatParameters new_params = initial_params;
287  new_params.set_log_search_progress(false);
288  new_params.set_max_number_of_conflicts(1);
289  new_params.set_max_deterministic_time(deterministic_time_limit);
290 
291  double elapsed_dtime = 0.0;
292 
293  const int num_times = 1000;
294  bool limit_reached = false;
295  auto* random = model->GetOrCreate<ModelRandomGenerator>();
296  for (int i = 0; i < num_times; ++i) {
297  if (time_limit->LimitReached() ||
298  elapsed_dtime > deterministic_time_limit) {
299  limit_reached = true;
300  break;
301  }
302 
303  // SetParameters() reset the deterministic time to zero inside time_limit.
304  sat_solver->SetParameters(new_params);
305  sat_solver->ResetDecisionHeuristic();
306  const SatSolver::Status result = sat_solver->SolveWithTimeLimit(time_limit);
307  elapsed_dtime += time_limit->GetElapsedDeterministicTime();
308 
309  if (result == SatSolver::FEASIBLE) {
310  LOG_IF(INFO, log_info) << "Trivial exploration found feasible solution!";
311  time_limit->AdvanceDeterministicTime(elapsed_dtime);
312  return true;
313  }
314 
315  if (!sat_solver->RestoreSolverToAssumptionLevel()) {
316  LOG_IF(INFO, log_info) << "UNSAT during trivial exploration heuristic.";
317  time_limit->AdvanceDeterministicTime(elapsed_dtime);
318  return false;
319  }
320 
321  // We randomize at the end so that the default params is executed
322  // at least once.
323  RandomizeDecisionHeuristic(random, &new_params);
324  new_params.set_random_seed(i);
325  new_params.set_max_deterministic_time(deterministic_time_limit -
326  elapsed_dtime);
327  }
328 
329  // Restore the initial parameters.
330  sat_solver->SetParameters(initial_params);
331  sat_solver->ResetDecisionHeuristic();
332  time_limit->AdvanceDeterministicTime(elapsed_dtime);
333  if (!sat_solver->RestoreSolverToAssumptionLevel()) return false;
334 
335  if (log_info) {
336  const int num_fixed = sat_solver->LiteralTrail().Index();
337  const int num_newly_fixed = num_fixed - initial_num_fixed;
338  const int num_variables = sat_solver->NumVariables();
339  LOG(INFO) << "Random exploration."
340  << " num_fixed: +" << num_newly_fixed << " (" << num_fixed << "/"
341  << num_variables << ")"
342  << " dtime: " << elapsed_dtime << "/" << deterministic_time_limit
343  << " wtime: " << wall_timer.Get()
344  << (limit_reached ? " (Aborted)" : "");
345  }
346  return sat_solver->FinishPropagation();
347 }
348 
351  wall_timer.Start();
352  options.log_info |= VLOG_IS_ON(1);
353 
354  // Reset the solver in case it was already used.
355  auto* sat_solver = model->GetOrCreate<SatSolver>();
356  sat_solver->SetAssumptionLevel(0);
357  if (!sat_solver->RestoreSolverToAssumptionLevel()) return false;
358 
359  // When called from Inprocessing, the implication graph should already be a
360  // DAG, so these two calls should return right away. But we do need them to
361  // get the topological order if this is used in isolation.
362  auto* implication_graph = model->GetOrCreate<BinaryImplicationGraph>();
363  if (!implication_graph->DetectEquivalences()) return false;
364  if (!sat_solver->FinishPropagation()) return false;
365 
366  auto* time_limit = model->GetOrCreate<TimeLimit>();
367  const int initial_num_fixed = sat_solver->LiteralTrail().Index();
368  const double initial_deterministic_time =
369  time_limit->GetElapsedDeterministicTime();
370  const double limit = initial_deterministic_time + options.deterministic_limit;
371 
372  const int num_variables = sat_solver->NumVariables();
373  SparseBitset<LiteralIndex> processed(LiteralIndex(2 * num_variables));
374 
375  int64 num_probed = 0;
376  int64 num_explicit_fix = 0;
377  int64 num_conflicts = 0;
378  int64 num_new_binary = 0;
379  int64 num_subsumed = 0;
380 
381  const auto& trail = *(model->Get<Trail>());
382  const auto& assignment = trail.Assignment();
383  auto* clause_manager = model->GetOrCreate<LiteralWatchers>();
384  const int id = implication_graph->PropagatorId();
385  const int clause_id = clause_manager->PropagatorId();
386 
387  // This is only needed when options.use_queue is true.
388  struct SavedNextLiteral {
389  LiteralIndex literal_index; // kNoLiteralIndex if we need to backtrack.
390  int rank; // Cached position_in_order, we prefer lower positions.
391 
392  bool operator<(const SavedNextLiteral& o) const { return rank < o.rank; }
393  };
394  std::vector<SavedNextLiteral> queue;
395  absl::StrongVector<LiteralIndex, int> position_in_order;
396 
397  // This is only needed when options use_queue is false;
399  if (!options.use_queue) starts.resize(2 * num_variables, 0);
400 
401  // We delay fixing of already assigned literal once we go back to level
402  // zero.
403  std::vector<Literal> to_fix;
404 
405  // Depending on the options. we do not use the same order.
406  // With tree look, it is better to start with "leaf" first since we try
407  // to reuse propagation as much as possible. This is also interesting to
408  // do when extracting binary clauses since we will need to propagate
409  // everyone anyway, and this should result in less clauses that can be
410  // removed later by transitive reduction.
411  //
412  // However, without tree-look and without the need to extract all binary
413  // clauses, it is better to just probe the root of the binary implication
414  // graph. This is exactly what happen when we probe using the topological
415  // order.
416  int order_index(0);
417  std::vector<LiteralIndex> probing_order =
418  implication_graph->ReverseTopologicalOrder();
419  if (!options.use_tree_look && !options.extract_binary_clauses) {
420  std::reverse(probing_order.begin(), probing_order.end());
421  }
422 
423  // We only use this for the queue version.
424  if (options.use_queue) {
425  position_in_order.assign(2 * num_variables, -1);
426  for (int i = 0; i < probing_order.size(); ++i) {
427  position_in_order[probing_order[i]] = i;
428  }
429  }
430 
431  while (!time_limit->LimitReached() &&
432  time_limit->GetElapsedDeterministicTime() <= limit) {
433  // We only enqueue literal at level zero if we don't use "tree look".
434  if (!options.use_tree_look) sat_solver->Backtrack(0);
435 
436  LiteralIndex next_decision = kNoLiteralIndex;
437  if (options.use_queue && sat_solver->CurrentDecisionLevel() > 0) {
438  // TODO(user): Instead of minimizing index in topo order (which might be
439  // nice for binary extraction), we could try to maximize reusability in
440  // some way.
441  const Literal prev_decision =
442  sat_solver->Decisions()[sat_solver->CurrentDecisionLevel() - 1]
443  .literal;
444  const auto& list =
445  implication_graph->Implications(prev_decision.Negated());
446  const int saved_queue_size = queue.size();
447  for (const Literal l : list) {
448  const Literal candidate = l.Negated();
449  if (processed[candidate.Index()]) continue;
450  if (position_in_order[candidate.Index()] == -1) continue;
451  if (assignment.LiteralIsAssigned(candidate)) {
452  if (assignment.LiteralIsFalse(candidate)) {
453  to_fix.push_back(Literal(candidate.Negated()));
454  }
455  continue;
456  }
457  queue.push_back(
458  {candidate.Index(), -position_in_order[candidate.Index()]});
459  }
460  std::sort(queue.begin() + saved_queue_size, queue.end());
461 
462  // Probe a literal that implies previous decision.
463  while (!queue.empty()) {
464  const LiteralIndex index = queue.back().literal_index;
465  queue.pop_back();
466  if (index == kNoLiteralIndex) {
467  // This is a backtrack marker, go back one level.
468  CHECK_GT(sat_solver->CurrentDecisionLevel(), 0);
469  sat_solver->Backtrack(sat_solver->CurrentDecisionLevel() - 1);
470  continue;
471  }
472  const Literal candidate(index);
473  if (processed[candidate.Index()]) continue;
474  if (assignment.LiteralIsAssigned(candidate)) {
475  if (assignment.LiteralIsFalse(candidate)) {
476  to_fix.push_back(Literal(candidate.Negated()));
477  }
478  continue;
479  }
480  next_decision = candidate.Index();
481  break;
482  }
483  }
484 
485  if (sat_solver->CurrentDecisionLevel() == 0) {
486  // Fix any delayed fixed literal.
487  for (const Literal literal : to_fix) {
488  if (!assignment.LiteralIsTrue(literal)) {
489  ++num_explicit_fix;
490  sat_solver->AddUnitClause(literal);
491  }
492  }
493  to_fix.clear();
494  if (!sat_solver->FinishPropagation()) return false;
495 
496  // Probe an unexplored node.
497  for (; order_index < probing_order.size(); ++order_index) {
498  const Literal candidate(probing_order[order_index]);
499  if (processed[candidate.Index()]) continue;
500  if (assignment.LiteralIsAssigned(candidate)) continue;
501  next_decision = candidate.Index();
502  break;
503  }
504 
505  // The pass is finished.
506  if (next_decision == kNoLiteralIndex) break;
507  } else if (next_decision == kNoLiteralIndex) {
508  const int level = sat_solver->CurrentDecisionLevel();
509  const Literal prev_decision = sat_solver->Decisions()[level - 1].literal;
510  const auto& list =
511  implication_graph->Implications(prev_decision.Negated());
512 
513  // Probe a literal that implies previous decision.
514  //
515  // Note that contrary to the queue based implementation, this do not
516  // process them in a particular order.
517  int j = starts[prev_decision.NegatedIndex()];
518  for (int i = 0; i < list.size(); ++i, ++j) {
519  j %= list.size();
520  const Literal candidate = Literal(list[j]).Negated();
521  if (processed[candidate.Index()]) continue;
522  if (assignment.LiteralIsFalse(candidate)) {
523  // candidate => previous => not(candidate), so we can fix it.
524  to_fix.push_back(Literal(candidate.Negated()));
525  continue;
526  }
527  // This shouldn't happen if extract_binary_clauses is false.
528  // We have an equivalence.
529  if (assignment.LiteralIsTrue(candidate)) continue;
530  next_decision = candidate.Index();
531  break;
532  }
533  starts[prev_decision.NegatedIndex()] = j;
534  if (next_decision == kNoLiteralIndex) {
535  sat_solver->Backtrack(level - 1);
536  continue;
537  }
538  }
539 
540  ++num_probed;
541  processed.Set(next_decision);
542  CHECK_NE(next_decision, kNoLiteralIndex);
543  queue.push_back({kNoLiteralIndex, 0}); // Backtrack marker.
544  const int level = sat_solver->CurrentDecisionLevel();
545  const int first_new_trail_index =
546  sat_solver->EnqueueDecisionAndBackjumpOnConflict(
547  Literal(next_decision));
548  const int new_level = sat_solver->CurrentDecisionLevel();
549  sat_solver->AdvanceDeterministicTime(time_limit);
550  if (sat_solver->IsModelUnsat()) return false;
551  if (new_level <= level) {
552  ++num_conflicts;
553 
554  // Sync the queue with the new level.
555  if (options.use_queue) {
556  if (new_level == 0) {
557  queue.clear();
558  } else {
559  int queue_level = level + 1;
560  while (queue_level > new_level) {
561  CHECK(!queue.empty());
562  if (queue.back().literal_index == kNoLiteralIndex) --queue_level;
563  queue.pop_back();
564  }
565  }
566  }
567 
568  // Fix next_decision to false if not already done.
569  //
570  // Even if we fixed something at evel zero, next_decision might not be
571  // fixed! But we can fix it. It can happen because when we propagate
572  // with clauses, we might have a => b but not not(b) => not(a). Like a
573  // => b and clause (not(a), not(b), c), propagating a will set c, but
574  // propagating not(c) will not do anything.
575  //
576  // We "delay" the fixing if we are not at level zero so that we can
577  // still reuse the current propagation work via tree look.
578  //
579  // TODO(user): Can we be smarter here? Maybe we can still fix the
580  // literal without going back to level zero by simply enqueing it with
581  // no reason? it will be bactracked over, but we will still lazily fix
582  // it later.
583  if (sat_solver->CurrentDecisionLevel() != 0 ||
584  assignment.LiteralIsFalse(Literal(next_decision))) {
585  to_fix.push_back(Literal(next_decision).Negated());
586  }
587  }
588 
589  // Inspect the newly propagated literals. Depending on the options, try to
590  // extract binary clauses via hyper binary resolution and/or mark the
591  // literals on the trail so that they do not need to be probed later.
592  if (new_level == 0) continue;
593  const Literal last_decision =
594  sat_solver->Decisions()[new_level - 1].literal;
595  int num_new_subsumed = 0;
596  for (int i = first_new_trail_index; i < trail.Index(); ++i) {
597  const Literal l = trail[i];
598  if (l == last_decision) continue;
599 
600  // If we can extract a binary clause that subsume the reason clause, we
601  // do add the binary and remove the subsumed clause.
602  //
603  // TODO(user): We could be slightly more generic and subsume some
604  // clauses that do not contains last_decision.Negated().
605  bool subsumed = false;
606  if (options.subsume_with_binary_clause &&
607  trail.AssignmentType(l.Variable()) == clause_id) {
608  for (const Literal lit : trail.Reason(l.Variable())) {
609  if (lit == last_decision.Negated()) {
610  subsumed = true;
611  break;
612  }
613  }
614  if (subsumed) {
615  ++num_new_subsumed;
616  ++num_new_binary;
617  implication_graph->AddBinaryClause(last_decision.Negated(), l);
618  const int trail_index = trail.Info(l.Variable()).trail_index;
619 
620  int test = 0;
621  for (const Literal lit :
622  clause_manager->ReasonClause(trail_index)->AsSpan()) {
623  if (lit == l) ++test;
624  if (lit == last_decision.Negated()) ++test;
625  }
626  CHECK_EQ(test, 2);
627  clause_manager->LazyDetach(clause_manager->ReasonClause(trail_index));
628 
629  // We need to change the reason now that the clause is cleared.
630  implication_graph->ChangeReason(trail_index, last_decision);
631  }
632  }
633 
634  if (options.extract_binary_clauses) {
635  // Anything not propagated by the BinaryImplicationGraph is a "new"
636  // binary clause. This is because the BinaryImplicationGraph has the
637  // highest priority of all propagators.
638  //
639  // Note(user): This is not 100% true, since when we launch the clause
640  // propagation for one literal we do finish it before calling again
641  // the binary propagation.
642  //
643  // TODO(user): Think about trying to extract clause that will not
644  // get removed by transitive reduction later. If we can both extract
645  // a => c and b => c , ideally we don't want to extract a => c first
646  // if we already know that a => b.
647  //
648  // TODO(user): Similar to previous point, we could find the LCA
649  // of all literals in the reason for this propagation. And use this
650  // as a reason for later hyber binary resolution. Like we do when
651  // this clause subsume the reason.
652  if (!subsumed && trail.AssignmentType(l.Variable()) != id) {
653  ++num_new_binary;
654  implication_graph->AddBinaryClause(last_decision.Negated(), l);
655  }
656  } else {
657  // If we don't extract binary, we don't need to explore any of
658  // these literal until more variables are fixed.
659  processed.Set(l.Index());
660  }
661  }
662 
663  // Inspect the watcher list for last_decision, If we have a blocking
664  // literal at true (implied by last decision), then we have subsumptions.
665  //
666  // The intuition behind this is that if a binary clause (a,b) subsume a
667  // clause, and we watch a.Negated() for this clause with a blocking
668  // literal b, then this watch entry will never change because we always
669  // propagate binary clauses first and the blocking literal will always be
670  // true. So after many propagations, we hope to have such configuration
671  // which is quite cheap to test here.
672  if (options.subsume_with_binary_clause) {
673  for (const auto& w :
674  clause_manager->WatcherListOnFalse(last_decision.Negated())) {
675  if (assignment.LiteralIsTrue(w.blocking_literal)) {
676  if (w.clause->empty()) continue;
677  CHECK_NE(w.blocking_literal, last_decision.Negated());
678 
679  // Add the binary clause if needed. Note that we change the reason
680  // to a binary one so that we never add the same clause twice.
681  //
682  // Tricky: while last_decision would be a valid reason, we need a
683  // reason that was assigned before this literal, so we use the
684  // decision at the level where this literal was assigne which is an
685  // even better reasony. Maybe it is just better to change all the
686  // reason above to a binary one so we don't have an issue here.
687  if (trail.AssignmentType(w.blocking_literal.Variable()) != id) {
688  ++num_new_binary;
689  implication_graph->AddBinaryClause(last_decision.Negated(),
690  w.blocking_literal);
691 
692  const auto& info = trail.Info(w.blocking_literal.Variable());
693  if (info.level > 0) {
694  const Literal d = sat_solver->Decisions()[info.level - 1].literal;
695  if (d != w.blocking_literal) {
696  implication_graph->ChangeReason(info.trail_index, d);
697  }
698  }
699  }
700 
701  ++num_new_subsumed;
702  clause_manager->LazyDetach(w.clause);
703  }
704  }
705  }
706 
707  if (num_new_subsumed > 0) {
708  // TODO(user): We might just want to do that even more lazily by
709  // checking for detached clause while propagating here? and do a big
710  // cleanup at the end.
711  clause_manager->CleanUpWatchers();
712  num_subsumed += num_new_subsumed;
713  }
714  }
715 
716  if (!sat_solver->ResetToLevelZero()) return false;
717  for (const Literal literal : to_fix) {
718  ++num_explicit_fix;
719  sat_solver->AddUnitClause(literal);
720  }
721  to_fix.clear();
722  if (!sat_solver->FinishPropagation()) return false;
723 
724  // Display stats.
725  const int num_fixed = sat_solver->LiteralTrail().Index();
726  const int num_newly_fixed = num_fixed - initial_num_fixed;
727  const double time_diff =
728  time_limit->GetElapsedDeterministicTime() - initial_deterministic_time;
729  const bool limit_reached = time_limit->LimitReached() ||
730  time_limit->GetElapsedDeterministicTime() > limit;
731  LOG_IF(INFO, options.log_info)
732  << "Probing. "
733  << " num_probed: " << num_probed << " num_fixed: +" << num_newly_fixed
734  << " (" << num_fixed << "/" << num_variables << ")"
735  << " explicit_fix:" << num_explicit_fix
736  << " num_conflicts:" << num_conflicts
737  << " new_binary_clauses: " << num_new_binary
738  << " subsumed: " << num_subsumed << " dtime: " << time_diff
739  << " wtime: " << wall_timer.Get() << (limit_reached ? " (Aborted)" : "");
740  return sat_solver->FinishPropagation();
741 }
742 
743 } // namespace sat
744 } // namespace operations_research
operations_research::sat::FailedLiteralProbingRound
bool FailedLiteralProbingRound(ProbingOptions options, Model *model)
Definition: probing.cc:349
var
IntVar * var
Definition: expr_array.cc:1858
operations_research::sat::Literal::Variable
BooleanVariable Variable() const
Definition: sat_base.h:80
operations_research::sat::Prober::ProbeBooleanVariables
bool ProbeBooleanVariables(double deterministic_time_limit, bool log_info=false)
Definition: probing.cc:40
operations_research::sat::SatSolver::SetAssumptionLevel
void SetAssumptionLevel(int assumption_level)
Definition: sat_solver.cc:962
INFO
const int INFO
Definition: log_severity.h:31
operations_research::sat::SatSolver::Status
Status
Definition: sat_solver.h:181
operations_research::sat::BinaryImplicationGraph
Definition: clause.h:456
min
int64 min
Definition: alldiff_cst.cc:138
operations_research::sat::IntegerTrail::AppendNewBounds
void AppendNewBounds(std::vector< IntegerLiteral > *output) const
Definition: integer.cc:1699
operations_research::sat::IntegerTrail
Definition: integer.h:533
operations_research::sat::kNoIntegerVariable
const IntegerVariable kNoIntegerVariable(-1)
operations_research::sat::VariableIsPositive
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:130
max
int64 max
Definition: alldiff_cst.cc:139
time_limit.h
operations_research::SparseBitset::ClearAndResize
void ClearAndResize(IntegerType size)
Definition: bitset.h:780
bound
int64 bound
Definition: routing_search.cc:971
LOG
#define LOG(severity)
Definition: base/logging.h:420
absl::StrongVector::size
size_type size() const
Definition: strong_vector.h:147
operations_research::sat::kNoLiteralIndex
const LiteralIndex kNoLiteralIndex(-1)
operations_research::sat::ProbingOptions::extract_binary_clauses
bool extract_binary_clauses
Definition: probing.h:163
WallTimer::Get
double Get() const
Definition: timer.h:45
operations_research::sat::SatSolver::AddBinaryClause
bool AddBinaryClause(Literal a, Literal b)
Definition: sat_solver.cc:180
operations_research::sat::Prober::ProbeOneVariable
bool ProbeOneVariable(BooleanVariable b)
Definition: probing.cc:178
operations_research::sat::SatPropagator::PropagatorId
int PropagatorId() const
Definition: sat_base.h:453
operations_research::sat::ProbingOptions
Definition: probing.h:122
operations_research::sat::SatSolver::EnqueueDecisionAndBackjumpOnConflict
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
Definition: sat_solver.cc:499
operations_research::sat::SatSolver
Definition: sat_solver.h:58
operations_research::sat::Prober::Prober
Prober(Model *model)
Definition: probing.cc:31
operations_research::sat::ModelRandomGenerator
Definition: sat/util.h:33
CHECK_GT
#define CHECK_GT(val1, val2)
Definition: base/logging.h:702
operations_research::sat::SatSolver::RestoreSolverToAssumptionLevel
bool RestoreSolverToAssumptionLevel()
Definition: sat_solver.cc:511
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
operations_research::sat::IntegerLiteral::GreaterOrEqual
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1247
operations_research::sat::PositiveVariable
IntegerVariable PositiveVariable(IntegerVariable i)
Definition: integer.h:134
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
sat_solver.h
operations_research::Domain::IntersectionWith
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
Definition: sorted_interval_list.cc:282
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
index
int index
Definition: pack.cc:508
sat_base.h
operations_research::sat::IntegerTrail::UpdateInitialDomain
bool UpdateInitialDomain(IntegerVariable var, Domain domain)
Definition: integer.cc:648
absl::StrongVector::assign
void assign(size_type n, const value_type &val)
Definition: strong_vector.h:131
operations_research::sat::SatSolver::AdvanceDeterministicTime
void AdvanceDeterministicTime(TimeLimit *limit)
Definition: sat_solver.h:423
operations_research::sat::ProbingOptions::deterministic_limit
double deterministic_limit
Definition: probing.h:143
clause.h
operations_research::sat::Trail::Index
int Index() const
Definition: sat_base.h:378
operations_research::SparseBitset::Set
void Set(IntegerType index)
Definition: bitset.h:805
operations_research::sat::LookForTrivialSatSolution
bool LookForTrivialSatSolution(double deterministic_time_limit, Model *model, bool log_info)
Definition: probing.cc:269
probing.h
operations_research::sat::SatSolver::CurrentDecisionLevel
int CurrentDecisionLevel() const
Definition: sat_solver.h:361
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
operations_research::SparseBitset< LiteralIndex >
operations_research::Assignment
An Assignment is a variable -> domains mapping, used to report solutions to the user.
Definition: constraint_solver.h:5033
operations_research::sat::ProbingOptions::log_info
bool log_info
Definition: probing.h:185
wall_timer
WallTimer * wall_timer
Definition: cp_model_solver.cc:2102
time_limit
SharedTimeLimit * time_limit
Definition: cp_model_solver.cc:2103
operations_research::sat::IntegerTrail::Enqueue
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:989
operations_research::SparseBitset::SparseClearAll
void SparseClearAll()
Definition: bitset.h:772
timer.h
operations_research::sat::LiteralWatchers
Definition: clause.h:160
operations_research::sat::IntegerTrail::InitialVariableDomain
const Domain & InitialVariableDomain(IntegerVariable var) const
Definition: integer.cc:644
operations_research::sat::kMaxIntegerValue
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
CHECK_EQ
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:697
operations_research::sat::Literal::Index
LiteralIndex Index() const
Definition: sat_base.h:84
operations_research::sat::Model
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
WallTimer
Definition: timer.h:23
LOG_IF
#define LOG_IF(severity, condition)
Definition: base/logging.h:479
operations_research::sat::ImpliedBounds
Definition: implied_bounds.h:77
implied_bounds.h
model
GRBmodel * model
Definition: gurobi_interface.cc:269
operations_research::sat::Literal
Definition: sat_base.h:64
absl::StrongVector< LiteralIndex, int >
operations_research::sat::kMinIntegerValue
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
util.h
iterator_adaptors.h
operations_research::sat::SatSolver::FinishPropagation
bool FinishPropagation()
Definition: sat_solver.cc:521
operations_research::sat::BinaryImplicationGraph::RepresentativeOf
Literal RepresentativeOf(Literal l) const
Definition: clause.h:561
operations_research::sat::ProbingOptions::subsume_with_binary_clause
bool subsume_with_binary_clause
Definition: probing.h:182
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
operations_research::sat::IntegerTrail::LowerBound
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1283
operations_research::TimeLimit::GetElapsedDeterministicTime
double GetElapsedDeterministicTime() const
Returns the elapsed deterministic time since the construction of this object.
Definition: time_limit.h:260
CHECK_NE
#define CHECK_NE(val1, val2)
Definition: base/logging.h:698
operations_research::sat::ProbingOptions::use_queue
bool use_queue
Definition: probing.h:176
operations_research::sat::Trail::AssignmentType
int AssignmentType(BooleanVariable var) const
Definition: sat_base.h:572
literal
Literal literal
Definition: optimization.cc:84
operations_research::sat::RandomizeDecisionHeuristic
void RandomizeDecisionHeuristic(URBG *random, SatParameters *parameters)
Definition: sat/util.h:76
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
operations_research::sat::ImpliedBounds::ProcessIntegerTrail
void ProcessIntegerTrail(Literal first_decision)
Definition: implied_bounds.cc:169
CHECK
#define CHECK(condition)
Definition: base/logging.h:495
operations_research::sat::SatSolver::NumVariables
int NumVariables() const
Definition: sat_solver.h:83
operations_research::sat::SatSolver::IsModelUnsat
bool IsModelUnsat() const
Definition: sat_solver.h:137
operations_research::sat::VariablesAssignment::LiteralIsAssigned
bool LiteralIsAssigned(Literal literal) const
Definition: sat_base.h:153
integer.h
operations_research::sat::Trail
Definition: sat_base.h:233
operations_research::sat::ProbingOptions::use_tree_look
bool use_tree_look
Definition: probing.h:170