21#include "absl/container/inlined_vector.h"
22#include "absl/types/span.h"
31#include "ortools/sat/sat_parameters.pb.h"
55 std::vector<BooleanVariable> bool_vars;
56 for (BooleanVariable
b(0);
b < num_variables; ++
b) {
61 bool_vars.push_back(
b);
66bool Prober::ProbeOneVariableInternal(BooleanVariable
b) {
67 new_integer_bounds_.clear();
73 const int saved_index = trail_.
Index();
82 for (
int i = saved_index + 1; i < trail_.
Index(); ++i) {
83 const Literal l = trail_[i];
86 if (decision.IsPositive()) {
87 propagated_.
Set(l.Index());
89 if (propagated_[l.Index()]) {
90 to_fix_at_true_.push_back(l);
99 new_binary_clauses_.push_back({decision.Negated(), l});
105 for (
const Literal l : to_fix_at_true_) {
108 to_fix_at_true_.clear();
110 num_new_binary_ += new_binary_clauses_.size();
111 for (
auto binary : new_binary_clauses_) {
114 new_binary_clauses_.clear();
130 std::sort(new_integer_bounds_.begin(), new_integer_bounds_.end(),
131 [](IntegerLiteral
a, IntegerLiteral
b) { return a.var < b.var; });
137 new_integer_bounds_.push_back(IntegerLiteral());
139 for (
int i = 0; i < new_integer_bounds_.size(); ++i) {
140 const IntegerVariable
var = new_integer_bounds_[i].var;
144 if (ub_min + 1 < lb_max) {
149 const Domain old_domain =
152 Domain(ub_min.value() + 1, lb_max.value() - 1).Complement());
153 if (new_domain != old_domain) {
174 if (i == 0 || new_integer_bounds_[i - 1].
var !=
var)
continue;
175 const IntegerValue new_bound =
std::min(new_integer_bounds_[i - 1].
bound,
176 new_integer_bounds_[i].
bound);
178 ++num_new_integer_bounds_;
200 if (!ProbeOneVariableInternal(
b))
return false;
204 num_new_literals_fixed_ += num_fixed - initial_num_fixed;
209 const double deterministic_time_limit,
210 absl::Span<const BooleanVariable> bool_vars) {
217 num_new_integer_bounds_ = 0;
218 num_new_literals_fixed_ = 0;
229 const double initial_deterministic_time =
231 const double limit = initial_deterministic_time + deterministic_time_limit;
233 bool limit_reached =
false;
236 for (
const BooleanVariable
b : bool_vars) {
246 limit_reached =
true;
252 if (!ProbeOneVariableInternal(
b)) {
259 num_new_literals_fixed_ = num_fixed - initial_num_fixed;
263 const double time_diff =
265 SOLVER_LOG(logger_,
"[Probing] deterministic_time: ", time_diff,
266 " (limit: ", deterministic_time_limit,
268 (limit_reached ?
"Aborted " :
""), num_probed,
"/",
269 bool_vars.size(),
")");
270 if (num_new_literals_fixed_ > 0) {
272 "[Probing] - new fixed Boolean: ", num_new_literals_fixed_,
273 " (", num_fixed,
"/", sat_solver_->
NumVariables(),
")");
275 if (num_new_holes_ > 0) {
276 SOLVER_LOG(logger_,
"[Probing] - new integer holes: ", num_new_holes_);
278 if (num_new_integer_bounds_ > 0) {
280 "[Probing] - new integer bounds: ", num_new_integer_bounds_);
282 if (num_new_binary_ > 0) {
283 SOLVER_LOG(logger_,
"[Probing] - new binary clause: ", num_new_binary_);
297 if (!sat_solver->RestoreSolverToAssumptionLevel())
return false;
300 const int initial_num_fixed = sat_solver->LiteralTrail().Index();
305 SatParameters initial_params = *
model->GetOrCreate<SatParameters>();
306 SatParameters new_params = initial_params;
307 new_params.set_log_search_progress(
false);
308 new_params.set_max_number_of_conflicts(1);
309 new_params.set_max_deterministic_time(deterministic_time_limit);
311 double elapsed_dtime = 0.0;
313 const int num_times = 1000;
314 bool limit_reached =
false;
316 for (
int i = 0; i < num_times; ++i) {
318 elapsed_dtime > deterministic_time_limit) {
319 limit_reached =
true;
324 sat_solver->SetParameters(new_params);
325 sat_solver->ResetDecisionHeuristic();
330 SOLVER_LOG(logger,
"Trivial exploration found feasible solution!");
335 if (!sat_solver->RestoreSolverToAssumptionLevel()) {
336 SOLVER_LOG(logger,
"UNSAT during trivial exploration heuristic.");
344 new_params.set_random_seed(i);
345 new_params.set_max_deterministic_time(deterministic_time_limit -
350 sat_solver->SetParameters(initial_params);
351 sat_solver->ResetDecisionHeuristic();
353 if (!sat_solver->RestoreSolverToAssumptionLevel())
return false;
355 if (logger->LoggingIsEnabled()) {
356 const int num_fixed = sat_solver->LiteralTrail().Index();
357 const int num_newly_fixed = num_fixed - initial_num_fixed;
358 const int num_variables = sat_solver->NumVariables();
359 SOLVER_LOG(logger,
"Random exploration.",
" num_fixed: +", num_newly_fixed,
360 " (", num_fixed,
"/", num_variables,
")",
361 " dtime: ", elapsed_dtime,
"/", deterministic_time_limit,
363 (limit_reached ?
" (Aborted)" :
""));
365 return sat_solver->FinishPropagation();
376 if (!sat_solver->RestoreSolverToAssumptionLevel())
return false;
382 if (!implication_graph->DetectEquivalences())
return false;
383 if (!sat_solver->FinishPropagation())
return false;
386 const int initial_num_fixed = sat_solver->LiteralTrail().Index();
387 const double initial_deterministic_time =
391 const int num_variables = sat_solver->NumVariables();
394 int64_t num_probed = 0;
395 int64_t num_explicit_fix = 0;
396 int64_t num_conflicts = 0;
397 int64_t num_new_binary = 0;
398 int64_t num_subsumed = 0;
401 const auto& assignment = trail.Assignment();
404 const int clause_id = clause_manager->PropagatorId();
407 struct SavedNextLiteral {
408 LiteralIndex literal_index;
411 bool operator<(
const SavedNextLiteral& o)
const {
return rank < o.rank; }
413 std::vector<SavedNextLiteral> queue;
422 std::vector<Literal> to_fix;
436 std::vector<LiteralIndex> probing_order =
437 implication_graph->ReverseTopologicalOrder();
439 std::reverse(probing_order.begin(), probing_order.end());
444 position_in_order.
assign(2 * num_variables, -1);
445 for (
int i = 0; i < probing_order.size(); ++i) {
446 position_in_order[probing_order[i]] = i;
456 if (options.
use_queue && sat_solver->CurrentDecisionLevel() > 0) {
461 sat_solver->Decisions()[sat_solver->CurrentDecisionLevel() - 1]
464 implication_graph->Implications(prev_decision.
Negated());
465 const int saved_queue_size = queue.size();
468 if (processed[candidate.
Index()])
continue;
469 if (position_in_order[candidate.
Index()] == -1)
continue;
470 if (assignment.LiteralIsAssigned(candidate)) {
471 if (assignment.LiteralIsFalse(candidate)) {
477 {candidate.
Index(), -position_in_order[candidate.
Index()]});
479 std::sort(queue.begin() + saved_queue_size, queue.end());
482 while (!queue.empty()) {
483 const LiteralIndex
index = queue.back().literal_index;
487 CHECK_GT(sat_solver->CurrentDecisionLevel(), 0);
488 sat_solver->Backtrack(sat_solver->CurrentDecisionLevel() - 1);
492 if (processed[candidate.
Index()])
continue;
493 if (assignment.LiteralIsAssigned(candidate)) {
494 if (assignment.LiteralIsFalse(candidate)) {
499 next_decision = candidate.
Index();
504 if (sat_solver->CurrentDecisionLevel() == 0) {
507 if (!assignment.LiteralIsTrue(
literal)) {
509 sat_solver->AddUnitClause(
literal);
513 if (!sat_solver->FinishPropagation())
return false;
516 for (; order_index < probing_order.size(); ++order_index) {
517 const Literal candidate(probing_order[order_index]);
518 if (processed[candidate.
Index()])
continue;
519 if (assignment.LiteralIsAssigned(candidate))
continue;
520 next_decision = candidate.
Index();
527 const int level = sat_solver->CurrentDecisionLevel();
528 const Literal prev_decision = sat_solver->Decisions()[level - 1].literal;
530 implication_graph->Implications(prev_decision.
Negated());
537 for (
int i = 0; i < list.size(); ++i, ++j) {
540 if (processed[candidate.
Index()])
continue;
541 if (assignment.LiteralIsFalse(candidate)) {
548 if (assignment.LiteralIsTrue(candidate))
continue;
549 next_decision = candidate.
Index();
554 sat_solver->Backtrack(level - 1);
560 processed.
Set(next_decision);
563 const int level = sat_solver->CurrentDecisionLevel();
564 const int first_new_trail_index =
565 sat_solver->EnqueueDecisionAndBackjumpOnConflict(
567 const int new_level = sat_solver->CurrentDecisionLevel();
568 sat_solver->AdvanceDeterministicTime(
time_limit);
569 if (sat_solver->IsModelUnsat())
return false;
570 if (new_level <= level) {
575 if (new_level == 0) {
578 int queue_level = level + 1;
579 while (queue_level > new_level) {
580 CHECK(!queue.empty());
602 if (sat_solver->CurrentDecisionLevel() != 0 ||
603 assignment.LiteralIsFalse(
Literal(next_decision))) {
604 to_fix.push_back(
Literal(next_decision).Negated());
611 if (new_level == 0)
continue;
613 sat_solver->Decisions()[new_level - 1].literal;
614 int num_new_subsumed = 0;
615 for (
int i = first_new_trail_index; i < trail.Index(); ++i) {
617 if (l == last_decision)
continue;
624 bool subsumed =
false;
626 trail.AssignmentType(l.
Variable()) == clause_id) {
628 if (lit == last_decision.
Negated()) {
636 implication_graph->AddBinaryClause(last_decision.
Negated(), l);
637 const int trail_index = trail.Info(l.
Variable()).trail_index;
641 clause_manager->ReasonClause(trail_index)->AsSpan()) {
642 if (lit == l) ++test;
643 if (lit == last_decision.
Negated()) ++test;
646 clause_manager->LazyDetach(clause_manager->ReasonClause(trail_index));
649 implication_graph->ChangeReason(trail_index, last_decision);
671 if (!subsumed && trail.AssignmentType(l.
Variable()) !=
id) {
673 implication_graph->AddBinaryClause(last_decision.
Negated(), l);
693 clause_manager->WatcherListOnFalse(last_decision.
Negated())) {
694 if (assignment.LiteralIsTrue(w.blocking_literal)) {
695 if (w.clause->empty())
continue;
706 if (trail.AssignmentType(w.blocking_literal.Variable()) !=
id) {
709 const auto& info = trail.Info(w.blocking_literal.Variable());
710 if (info.level > 0) {
712 implication_graph->AddBinaryClause(last_decision.
Negated(),
715 const Literal d = sat_solver->Decisions()[info.level - 1].literal;
716 if (d != w.blocking_literal) {
717 implication_graph->ChangeReason(info.trail_index, d);
723 clause_manager->LazyDetach(w.clause);
728 if (num_new_subsumed > 0) {
732 clause_manager->CleanUpWatchers();
733 num_subsumed += num_new_subsumed;
737 if (!sat_solver->ResetToLevelZero())
return false;
740 sat_solver->AddUnitClause(
literal);
743 if (!sat_solver->FinishPropagation())
return false;
746 const int num_fixed = sat_solver->LiteralTrail().
Index();
747 const int num_newly_fixed = num_fixed - initial_num_fixed;
748 const double time_diff =
754 <<
" num_probed: " << num_probed <<
" num_fixed: +" << num_newly_fixed
755 <<
" (" << num_fixed <<
"/" << num_variables <<
")"
756 <<
" explicit_fix:" << num_explicit_fix
757 <<
" num_conflicts:" << num_conflicts
758 <<
" new_binary_clauses: " << num_new_binary
759 <<
" subsumed: " << num_subsumed <<
" dtime: " << time_diff
760 <<
" wtime: " <<
wall_timer.
Get() << (limit_reached ?
" (Aborted)" :
"");
761 return sat_solver->FinishPropagation();
#define LOG_IF(severity, condition)
#define CHECK_EQ(val1, val2)
#define CHECK_GT(val1, val2)
#define CHECK_NE(val1, val2)
void assign(size_type n, const value_type &val)
void resize(size_type new_size)
An Assignment is a variable -> domains mapping, used to report solutions to the user.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
double GetElapsedDeterministicTime() const
bool LimitReached() const
void AdvanceDeterministicTime(double deterministic_duration)
bool LoggingIsEnabled() const
void Set(IntegerType index)
void ClearAndResize(IntegerType size)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
double GetElapsedDeterministicTime() const
Returns the elapsed deterministic time since the construction of this object.
Literal RepresentativeOf(Literal l) const
void ProcessIntegerTrail(Literal first_decision)
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
void AppendNewBounds(std::vector< IntegerLiteral > *output) const
IntegerValue LowerBound(IntegerVariable i) const
const Domain & InitialVariableDomain(IntegerVariable var) const
bool UpdateInitialDomain(IntegerVariable var, Domain domain)
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
BooleanVariable Variable() const
Class that owns everything related to a particular optimization model.
bool ProbeOneVariable(BooleanVariable b)
bool ProbeBooleanVariables(double deterministic_time_limit)
const Trail & LiteralTrail() const
void SetAssumptionLevel(int assumption_level)
void AdvanceDeterministicTime(TimeLimit *limit)
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
bool AddBinaryClause(Literal a, Literal b)
bool RestoreSolverToAssumptionLevel()
bool IsModelUnsat() const
int CurrentDecisionLevel() const
bool AddUnitClause(Literal true_literal)
int AssignmentType(BooleanVariable var) const
bool LiteralIsAssigned(Literal literal) const
ModelSharedTimeLimit * time_limit
void RandomizeDecisionHeuristic(absl::BitGenRef random, SatParameters *parameters)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
bool LookForTrivialSatSolution(double deterministic_time_limit, Model *model)
const LiteralIndex kNoLiteralIndex(-1)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
bool FailedLiteralProbingRound(ProbingOptions options, Model *model)
bool VariableIsPositive(IntegerVariable i)
Collection of objects used to extend the Constraint Solver library.
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
double deterministic_limit
bool subsume_with_binary_clause
bool extract_binary_clauses
#define SOLVER_LOG(logger,...)
#define VLOG_IS_ON(verboselevel)