23#include "absl/log/check.h"
24#include "absl/types/span.h"
29#include "ortools/sat/sat_parameters.pb.h"
40 : parameters_(*(model->GetOrCreate<SatParameters>())),
41 trail_(*model->GetOrCreate<
Trail>()),
46 const int old_num_variables = activities_.size();
47 DCHECK_GE(num_variables, activities_.size());
49 activities_.resize(num_variables, parameters_.initial_variables_activity());
50 tie_breakers_.resize(num_variables, 0.0);
52 pq_need_update_for_var_at_trail_index_.IncreaseSize(num_variables);
54 has_forced_polarity_.resize(num_variables,
false);
55 forced_polarity_.resize(num_variables);
56 has_target_polarity_.resize(num_variables,
false);
57 target_polarity_.resize(num_variables);
58 var_polarity_.resize(num_variables);
60 ResetInitialPolarity(old_num_variables);
64 var_ordering_.Reserve(num_variables);
65 if (var_ordering_is_initialized_) {
66 for (BooleanVariable var(old_num_variables); var < num_variables; ++var) {
67 var_ordering_.Add({var, 0.0, activities_[var]});
73 if (parameters_.use_erwa_heuristic()) {
75 num_conflicts_stack_.push_back({trail_.Index(), 1});
78 if (trail_index > target_length_) {
79 target_length_ = trail_index;
80 has_target_polarity_.assign(has_target_polarity_.size(),
false);
81 for (
int i = 0;
i < trail_index; ++
i) {
83 has_target_polarity_[l.
Variable()] =
true;
88 if (trail_index > best_partial_assignment_.size()) {
89 best_partial_assignment_.assign(trail_.IteratorAt(0),
90 trail_.IteratorAt(trail_index));
93 --num_conflicts_until_rephase_;
97void SatDecisionPolicy::RephaseIfNeeded() {
98 if (parameters_.polarity_rephase_increment() <= 0)
return;
99 if (num_conflicts_until_rephase_ > 0)
return;
101 VLOG(1) <<
"End of polarity phase " << polarity_phase_
102 <<
" target_length: " << target_length_
103 <<
" best_length: " << best_partial_assignment_.size();
106 num_conflicts_until_rephase_ =
107 parameters_.polarity_rephase_increment() * (polarity_phase_ + 1);
111 has_target_polarity_.
assign(has_target_polarity_.size(),
false);
116 switch (polarity_phase_ % 8) {
118 ResetInitialPolarity(0);
121 UseLongestAssignmentAsInitialPolarity();
124 ResetInitialPolarity(0,
true);
127 UseLongestAssignmentAsInitialPolarity();
130 RandomizeCurrentPolarity();
133 UseLongestAssignmentAsInitialPolarity();
136 FlipCurrentPolarity();
139 if (UseLsSolutionAsInitialPolarity())
break;
140 UseLongestAssignmentAsInitialPolarity();
146 const int num_variables = activities_.size();
147 variable_activity_increment_ = 1.0;
148 activities_.assign(num_variables, parameters_.initial_variables_activity());
149 tie_breakers_.assign(num_variables, 0.0);
151 var_ordering_.Clear();
154 num_conflicts_until_rephase_ = parameters_.polarity_rephase_increment();
156 ResetInitialPolarity(0);
157 has_target_polarity_.assign(num_variables,
false);
158 has_forced_polarity_.assign(num_variables,
false);
159 best_partial_assignment_.clear();
162 num_conflicts_stack_.clear();
164 var_ordering_is_initialized_ =
false;
167void SatDecisionPolicy::ResetInitialPolarity(
int from,
bool inverted) {
169 const int num_variables = activities_.size();
170 for (BooleanVariable var(from); var < num_variables; ++var) {
171 switch (parameters_.initial_polarity()) {
172 case SatParameters::POLARITY_TRUE:
173 var_polarity_[var] = inverted ? false :
true;
175 case SatParameters::POLARITY_FALSE:
176 var_polarity_[var] = inverted ? true :
false;
178 case SatParameters::POLARITY_RANDOM:
179 var_polarity_[var] = std::uniform_int_distribution<int>(0, 1)(*random_);
185void SatDecisionPolicy::UseLongestAssignmentAsInitialPolarity() {
189 for (
const Literal l : best_partial_assignment_) {
190 var_polarity_[l.Variable()] = l.IsPositive();
192 best_partial_assignment_.clear();
195bool SatDecisionPolicy::UseLsSolutionAsInitialPolarity() {
196 if (!parameters_.polarity_exploit_ls_hints())
return false;
198 if (ls_hints_->NumSolutions() == 0)
return false;
203 std::shared_ptr<const SharedLsSolutionRepository::Solution>
solution =
204 ls_hints_->GetRandomBiasedSolution(*random_);
205 if (
solution->variable_values.size() != var_polarity_.size())
return false;
207 for (
int i = 0;
i <
solution->variable_values.size(); ++
i) {
208 var_polarity_[BooleanVariable(
i)] =
solution->variable_values[
i] == 1;
214void SatDecisionPolicy::FlipCurrentPolarity() {
215 const int num_variables = var_polarity_.size();
216 for (BooleanVariable var; var < num_variables; ++var) {
217 var_polarity_[var] = !var_polarity_[var];
221void SatDecisionPolicy::RandomizeCurrentPolarity() {
222 const int num_variables = var_polarity_.size();
223 for (BooleanVariable var; var < num_variables; ++var) {
224 var_polarity_[var] = std::uniform_int_distribution<int>(0, 1)(*random_);
228void SatDecisionPolicy::InitializeVariableOrdering() {
229 const int num_variables = activities_.size();
233 var_ordering_.Clear();
234 tmp_variables_.clear();
235 for (BooleanVariable var(0); var < num_variables; ++var) {
236 if (!trail_.Assignment().VariableIsAssigned(var)) {
237 if (activities_[var] > 0.0) {
238 var_ordering_.Add({var, tie_breakers_[var], activities_[var]});
240 tmp_variables_.push_back(var);
251 switch (parameters_.preferred_variable_order()) {
252 case SatParameters::IN_ORDER:
254 case SatParameters::IN_REVERSE_ORDER:
255 std::reverse(tmp_variables_.begin(), tmp_variables_.end());
257 case SatParameters::IN_RANDOM_ORDER:
258 std::shuffle(tmp_variables_.begin(), tmp_variables_.end(), *random_);
263 for (
const BooleanVariable var : tmp_variables_) {
264 var_ordering_.Add({var, tie_breakers_[var], 0.0});
268 pq_need_update_for_var_at_trail_index_.ClearAndResize(num_variables);
269 pq_need_update_for_var_at_trail_index_.SetAllBefore(trail_.Index());
270 var_ordering_is_initialized_ =
true;
274 if (!parameters_.use_optimization_hints())
return;
275 DCHECK_GE(weight, 0.0);
276 DCHECK_LE(weight, 1.0);
278 has_forced_polarity_[literal.
Variable()] =
true;
283 tie_breakers_[literal.
Variable()] = weight;
284 var_ordering_is_initialized_ =
false;
289 std::vector<std::pair<Literal, float>> prefs;
290 for (BooleanVariable var(0); var < var_polarity_.size(); ++var) {
293 const float value = var_ordering_.GetElement(var.value()).tie_breaker;
295 prefs.push_back(std::make_pair(
Literal(var, var_polarity_[var]), value));
302 absl::Span<const Literal> literals) {
303 if (parameters_.use_erwa_heuristic()) {
304 if (num_bumps_.size() != activities_.size()) {
305 num_bumps_.resize(activities_.size(), 0);
307 for (
const Literal literal : literals) {
311 ++num_bumps_[literal.Variable()];
316 const double max_activity_value = parameters_.max_variable_activity_value();
317 for (
const Literal literal : literals) {
318 const BooleanVariable var = literal.Variable();
319 const int level = trail_.Info(var).level;
320 if (level == 0)
continue;
321 activities_[var] += variable_activity_increment_;
322 pq_need_update_for_var_at_trail_index_.Set(trail_.Info(var).trail_index);
323 if (activities_[var] > max_activity_value) {
324 RescaleVariableActivities(1.0 / max_activity_value);
329void SatDecisionPolicy::RescaleVariableActivities(
double scaling_factor) {
330 variable_activity_increment_ *= scaling_factor;
331 for (BooleanVariable var(0); var < activities_.size(); ++var) {
332 activities_[var] *= scaling_factor;
346 var_ordering_is_initialized_ =
false;
350 variable_activity_increment_ *= 1.0 / parameters_.variable_activity_decay();
355 if (!var_ordering_is_initialized_) {
356 InitializeVariableOrdering();
361 const double ratio = parameters_.random_branches_ratio();
362 auto zero_to_one = [
this]() {
363 return std::uniform_real_distribution<double>()(*random_);
365 if (ratio != 0.0 && zero_to_one() < ratio) {
369 std::uniform_int_distribution<int> index_dist(0,
370 var_ordering_.Size() - 1);
371 var = var_ordering_.QueueElement(index_dist(*random_)).var;
372 if (!trail_.Assignment().VariableIsAssigned(var))
break;
373 pq_need_update_for_var_at_trail_index_.Set(trail_.Info(var).trail_index);
374 var_ordering_.Remove(var.value());
378 DCHECK(!var_ordering_.IsEmpty());
379 var = var_ordering_.Top().var;
380 while (trail_.Assignment().VariableIsAssigned(var)) {
382 pq_need_update_for_var_at_trail_index_.Set(trail_.Info(var).trail_index);
383 DCHECK(!var_ordering_.IsEmpty());
384 var = var_ordering_.Top().var;
389 const double random_ratio = parameters_.random_polarity_ratio();
390 if (random_ratio != 0.0 && zero_to_one() < random_ratio) {
391 return Literal(var, std::uniform_int_distribution<int>(0, 1)(*random_));
394 if (has_forced_polarity_[var])
return Literal(var, forced_polarity_[var]);
395 if (in_stable_phase_ && has_target_polarity_[var]) {
396 return Literal(var, target_polarity_[var]);
398 return Literal(var, var_polarity_[var]);
401void SatDecisionPolicy::PqInsertOrUpdate(BooleanVariable var) {
402 const WeightedVarQueueElement element{var, tie_breakers_[var],
404 if (var_ordering_.
Contains(var.value())) {
408 var_ordering_.
Add(element);
414 if (maybe_enable_phase_saving_ && parameters_.use_phase_saving()) {
415 for (
int i = target_trail_index;
i < trail_.Index(); ++
i) {
421 DCHECK_LT(target_trail_index, trail_.Index());
422 if (parameters_.use_erwa_heuristic()) {
423 if (num_bumps_.size() != activities_.size()) {
424 num_bumps_.resize(activities_.size(), 0);
429 const double alpha = std::max(0.06, 0.4 - 1e-6 * num_conflicts_);
433 int num_conflicts = 0;
434 int next_num_conflicts_update =
435 num_conflicts_stack_.empty() ? -1
436 : num_conflicts_stack_.back().trail_index;
438 int trail_index = trail_.Index();
439 while (trail_index > target_trail_index) {
440 if (next_num_conflicts_update == trail_index) {
441 num_conflicts += num_conflicts_stack_.back().count;
442 num_conflicts_stack_.pop_back();
443 next_num_conflicts_update =
444 num_conflicts_stack_.empty()
446 : num_conflicts_stack_.back().trail_index;
448 const BooleanVariable var = trail_[--trail_index].Variable();
452 if (num_conflicts > 0) {
453 const int64_t num_bumps = num_bumps_[var];
454 double new_rate = 0.0;
457 new_rate =
static_cast<double>(num_bumps) / num_conflicts;
459 activities_[var] = alpha * new_rate + (1 - alpha) * activities_[var];
461 if (var_ordering_is_initialized_) PqInsertOrUpdate(var);
463 if (num_conflicts > 0) {
464 if (!num_conflicts_stack_.empty() &&
465 num_conflicts_stack_.back().trail_index == trail_.Index()) {
466 num_conflicts_stack_.back().count += num_conflicts;
468 num_conflicts_stack_.push_back({trail_.Index(), num_conflicts});
472 if (!var_ordering_is_initialized_)
return;
475 int to_update = pq_need_update_for_var_at_trail_index_.Top();
476 while (to_update >= target_trail_index) {
477 DCHECK_LT(to_update, trail_.Index());
478 PqInsertOrUpdate(trail_[to_update].
Variable());
479 pq_need_update_for_var_at_trail_index_.ClearTop();
480 to_update = pq_need_update_for_var_at_trail_index_.Top();
485 if (
DEBUG_MODE && var_ordering_is_initialized_) {
486 for (
int trail_index = trail_.Index() - 1; trail_index > target_trail_index;
488 const BooleanVariable var = trail_[trail_index].Variable();
489 CHECK(var_ordering_.Contains(var.value()));
490 CHECK_EQ(activities_[var], var_ordering_.GetElement(var.value()).weight);
void Add(Element element)
void IncreasePriority(Element element)
Optimized version of ChangePriority() when we know the direction.
bool Contains(int index) const
Returns true if the element with given index is currently in the queue.
BooleanVariable Variable() const
void BumpVariableActivities(absl::Span< const Literal > literals)
std::vector< std::pair< Literal, float > > AllPreferences() const
Returns the vector of the current assignment preferences.
void SetAssignmentPreference(Literal literal, float weight)
void Untrail(int target_trail_index)
Called on Untrail() so that we can update the set of possible decisions.
void IncreaseNumVariables(int num_variables)
void ResetDecisionHeuristic()
SatDecisionPolicy(Model *model)
void UpdateVariableActivityIncrement()
void BeforeConflict(int trail_index)
void assign(size_type n, const value_type &val)
In SWIG mode, we don't want anything besides these top-level includes.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid