24#include "absl/algorithm/container.h"
25#include "absl/log/check.h"
26#include "absl/types/span.h"
43 trail_(*model->GetOrCreate<
Trail>()),
48 const int old_num_variables = activities_.size();
49 DCHECK_GE(num_variables, activities_.size());
51 activities_.resize(num_variables, parameters_.initial_variables_activity());
52 tie_breakers_.resize(num_variables, 0.0);
54 pq_need_update_for_var_at_trail_index_.IncreaseSize(num_variables);
56 has_forced_polarity_.resize(num_variables,
false);
57 forced_polarity_.resize(num_variables);
58 has_target_polarity_.resize(num_variables,
false);
59 target_polarity_.resize(num_variables);
60 var_polarity_.resize(num_variables);
62 ResetInitialPolarity(old_num_variables);
66 var_ordering_.Reserve(num_variables);
67 if (var_ordering_is_initialized_) {
68 for (BooleanVariable var(old_num_variables); var < num_variables; ++var) {
69 var_ordering_.Add({var, 0.0, activities_[var]});
75 if (parameters_.use_erwa_heuristic()) {
77 num_conflicts_stack_.push_back({trail_.Index(), 1});
80 if (trail_index > target_length_) {
81 target_length_ = trail_index;
82 has_target_polarity_.assign(has_target_polarity_.size(),
false);
83 for (
int i = 0;
i < trail_index; ++
i) {
85 has_target_polarity_[l.
Variable()] =
true;
90 if (trail_index > best_partial_assignment_.size()) {
91 best_partial_assignment_.assign(trail_.IteratorAt(0),
92 trail_.IteratorAt(trail_index));
95 --num_conflicts_until_rephase_;
99void SatDecisionPolicy::RephaseIfNeeded() {
101 if (num_conflicts_until_rephase_ > 0)
return;
103 VLOG(1) <<
"End of polarity phase " << polarity_phase_
104 <<
" target_length: " << target_length_
105 <<
" best_length: " << best_partial_assignment_.size();
108 num_conflicts_until_rephase_ =
113 has_target_polarity_.
assign(has_target_polarity_.size(),
false);
118 switch (polarity_phase_ % 8) {
120 ResetInitialPolarity(0);
123 UseLongestAssignmentAsInitialPolarity();
126 ResetInitialPolarity(0,
true);
129 UseLongestAssignmentAsInitialPolarity();
132 RandomizeCurrentPolarity();
135 UseLongestAssignmentAsInitialPolarity();
138 FlipCurrentPolarity();
141 if (UseLsSolutionAsInitialPolarity())
break;
142 UseLongestAssignmentAsInitialPolarity();
148 const int num_variables = activities_.size();
149 variable_activity_increment_ = 1.0;
150 activities_.assign(num_variables, parameters_.initial_variables_activity());
151 tie_breakers_.assign(num_variables, 0.0);
153 var_ordering_.Clear();
156 num_conflicts_until_rephase_ = parameters_.polarity_rephase_increment();
158 ResetInitialPolarity(0);
159 has_target_polarity_.assign(num_variables,
false);
160 has_forced_polarity_.assign(num_variables,
false);
161 best_partial_assignment_.clear();
164 num_conflicts_stack_.clear();
166 var_ordering_is_initialized_ =
false;
169void SatDecisionPolicy::ResetInitialPolarity(
int from,
bool inverted) {
171 const int num_variables = activities_.size();
172 for (BooleanVariable var(from); var < num_variables; ++var) {
175 var_polarity_[var] = inverted ? false :
true;
178 var_polarity_[var] = inverted ? true :
false;
181 var_polarity_[var] = std::uniform_int_distribution<int>(0, 1)(*random_);
187void SatDecisionPolicy::UseLongestAssignmentAsInitialPolarity() {
191 for (
const Literal l : best_partial_assignment_) {
192 var_polarity_[l.Variable()] = l.IsPositive();
194 best_partial_assignment_.clear();
197bool SatDecisionPolicy::UseLsSolutionAsInitialPolarity() {
198 if (!parameters_.polarity_exploit_ls_hints())
return false;
200 if (ls_hints_->NumSolutions() == 0)
return false;
205 std::shared_ptr<const SharedLsSolutionRepository::Solution>
solution =
206 ls_hints_->GetRandomBiasedSolution(*random_);
207 if (
solution->variable_values.size() != var_polarity_.size())
return false;
209 for (
int i = 0;
i <
solution->variable_values.size(); ++
i) {
210 var_polarity_[BooleanVariable(
i)] =
solution->variable_values[
i] == 1;
216void SatDecisionPolicy::FlipCurrentPolarity() {
217 const int num_variables = var_polarity_.size();
218 for (BooleanVariable var; var < num_variables; ++var) {
219 var_polarity_[var] = !var_polarity_[var];
223void SatDecisionPolicy::RandomizeCurrentPolarity() {
224 const int num_variables = var_polarity_.size();
225 for (BooleanVariable var; var < num_variables; ++var) {
226 var_polarity_[var] = std::uniform_int_distribution<int>(0, 1)(*random_);
231 DCHECK_EQ(trail_.CurrentDecisionLevel(), 0);
232 CHECK(!activities_.empty());
233 const double max_activity =
234 *absl::c_max_element(activities_) + variable_activity_increment_;
235 const double kDecay = 0.999;
236 variable_activity_increment_ =
237 max_activity / pow(kDecay, best_partial_assignment_.size() + 1);
238 var_ordering_is_initialized_ =
false;
239 if (max_activity + variable_activity_increment_ >
240 parameters_.max_variable_activity_value()) {
241 RescaleVariableActivities(1 / parameters_.max_variable_activity_value());
244 for (
int i = 0;
i < best_partial_assignment_.size(); ++
i) {
245 const Literal l = best_partial_assignment_[
i];
247 activities_[l.
Variable()] += weight * variable_activity_increment_;
251void SatDecisionPolicy::InitializeVariableOrdering() {
252 const int num_variables = activities_.size();
256 var_ordering_.
Clear();
257 tmp_variables_.clear();
258 for (BooleanVariable var(0); var < num_variables; ++var) {
260 if (activities_[var] > 0.0) {
261 var_ordering_.
Add({var, tie_breakers_[var], activities_[var]});
263 tmp_variables_.push_back(var);
274 switch (parameters_.preferred_variable_order()) {
278 std::reverse(tmp_variables_.begin(), tmp_variables_.end());
281 std::shuffle(tmp_variables_.begin(), tmp_variables_.end(), *random_);
286 for (
const BooleanVariable var : tmp_variables_) {
287 var_ordering_.Add({var, tie_breakers_[var], 0.0});
291 pq_need_update_for_var_at_trail_index_.ClearAndResize(num_variables);
292 pq_need_update_for_var_at_trail_index_.SetAllBefore(trail_.Index());
293 var_ordering_is_initialized_ =
true;
297 if (!parameters_.use_optimization_hints())
return;
298 DCHECK_GE(weight, 0.0);
299 DCHECK_LE(weight, 1.0);
301 has_forced_polarity_[literal.
Variable()] =
true;
306 tie_breakers_[literal.
Variable()] = weight;
307 var_ordering_is_initialized_ =
false;
312 std::vector<std::pair<Literal, float>> prefs;
313 for (BooleanVariable var(0); var < var_polarity_.size(); ++var) {
316 const float value = var_ordering_.GetElement(var.value()).tie_breaker;
318 prefs.push_back(std::make_pair(
Literal(var, var_polarity_[var]), value));
325 absl::Span<const Literal> literals) {
326 if (parameters_.use_erwa_heuristic()) {
327 if (num_bumps_.size() != activities_.size()) {
328 num_bumps_.resize(activities_.size(), 0);
330 for (
const Literal literal : literals) {
334 ++num_bumps_[literal.Variable()];
339 const double max_activity_value = parameters_.max_variable_activity_value();
340 for (
const Literal literal : literals) {
341 const BooleanVariable var = literal.Variable();
342 const int level = trail_.Info(var).level;
343 if (level == 0)
continue;
344 activities_[var] += variable_activity_increment_;
345 pq_need_update_for_var_at_trail_index_.Set(trail_.Info(var).trail_index);
346 if (activities_[var] > max_activity_value) {
347 RescaleVariableActivities(1.0 / max_activity_value);
352void SatDecisionPolicy::RescaleVariableActivities(
double scaling_factor) {
353 variable_activity_increment_ *= scaling_factor;
354 for (BooleanVariable var(0); var < activities_.size(); ++var) {
355 activities_[var] *= scaling_factor;
369 var_ordering_is_initialized_ =
false;
373 variable_activity_increment_ *= 1.0 / parameters_.variable_activity_decay();
378 if (!var_ordering_is_initialized_) {
379 InitializeVariableOrdering();
384 const double ratio = parameters_.random_branches_ratio();
385 auto zero_to_one = [
this]() {
386 return std::uniform_real_distribution<double>()(*random_);
388 if (ratio != 0.0 && zero_to_one() < ratio) {
392 std::uniform_int_distribution<int> index_dist(0,
393 var_ordering_.Size() - 1);
394 var = var_ordering_.QueueElement(index_dist(*random_)).var;
395 if (!trail_.Assignment().VariableIsAssigned(var))
break;
396 pq_need_update_for_var_at_trail_index_.Set(trail_.Info(var).trail_index);
397 var_ordering_.Remove(var.value());
401 DCHECK(!var_ordering_.IsEmpty());
402 var = var_ordering_.Top().var;
403 while (trail_.Assignment().VariableIsAssigned(var)) {
405 pq_need_update_for_var_at_trail_index_.Set(trail_.Info(var).trail_index);
406 DCHECK(!var_ordering_.IsEmpty());
407 var = var_ordering_.Top().var;
412 const double random_ratio = parameters_.random_polarity_ratio();
413 if (random_ratio != 0.0 && zero_to_one() < random_ratio) {
414 return Literal(var, std::uniform_int_distribution<int>(0, 1)(*random_));
417 if (has_forced_polarity_[var])
return Literal(var, forced_polarity_[var]);
418 if (in_stable_phase_ && has_target_polarity_[var]) {
419 return Literal(var, target_polarity_[var]);
421 return Literal(var, var_polarity_[var]);
424void SatDecisionPolicy::PqInsertOrUpdate(BooleanVariable var) {
425 const WeightedVarQueueElement element{var, tie_breakers_[var],
427 if (var_ordering_.
Contains(var.value())) {
431 var_ordering_.
Add(element);
437 if (maybe_enable_phase_saving_ && parameters_.use_phase_saving()) {
438 for (
int i = target_trail_index;
i < trail_.Index(); ++
i) {
444 DCHECK_LT(target_trail_index, trail_.Index());
445 if (parameters_.use_erwa_heuristic()) {
446 if (num_bumps_.size() != activities_.size()) {
447 num_bumps_.resize(activities_.size(), 0);
452 const double alpha = std::max(0.06, 0.4 - 1e-6 * num_conflicts_);
456 int num_conflicts = 0;
457 int next_num_conflicts_update =
458 num_conflicts_stack_.empty() ? -1
459 : num_conflicts_stack_.back().trail_index;
461 int trail_index = trail_.Index();
462 while (trail_index > target_trail_index) {
463 if (next_num_conflicts_update == trail_index) {
464 num_conflicts += num_conflicts_stack_.back().count;
465 num_conflicts_stack_.pop_back();
466 next_num_conflicts_update =
467 num_conflicts_stack_.empty()
469 : num_conflicts_stack_.back().trail_index;
471 const BooleanVariable var = trail_[--trail_index].Variable();
475 if (num_conflicts > 0) {
476 const int64_t num_bumps = num_bumps_[var];
477 double new_rate = 0.0;
480 new_rate =
static_cast<double>(num_bumps) / num_conflicts;
482 activities_[var] = alpha * new_rate + (1 - alpha) * activities_[var];
484 if (var_ordering_is_initialized_) PqInsertOrUpdate(var);
486 if (num_conflicts > 0) {
487 if (!num_conflicts_stack_.empty() &&
488 num_conflicts_stack_.back().trail_index == trail_.Index()) {
489 num_conflicts_stack_.back().count += num_conflicts;
491 num_conflicts_stack_.push_back({trail_.Index(), num_conflicts});
495 if (!var_ordering_is_initialized_)
return;
498 int to_update = pq_need_update_for_var_at_trail_index_.Top();
499 while (to_update >= target_trail_index) {
500 DCHECK_LT(to_update, trail_.Index());
501 PqInsertOrUpdate(trail_[to_update].
Variable());
502 pq_need_update_for_var_at_trail_index_.ClearTop();
503 to_update = pq_need_update_for_var_at_trail_index_.Top();
508 if (
DEBUG_MODE && var_ordering_is_initialized_) {
509 for (
int trail_index = trail_.Index() - 1; trail_index > target_trail_index;
511 const BooleanVariable var = trail_[trail_index].Variable();
512 CHECK(var_ordering_.Contains(var.value()));
513 CHECK_EQ(activities_[var], var_ordering_.GetElement(var.value()).weight);
void Add(Element element)
void IncreasePriority(Element element)
bool Contains(int index) const
BooleanVariable Variable() const
void BumpVariableActivities(absl::Span< const Literal > literals)
std::vector< std::pair< Literal, float > > AllPreferences() const
void SetAssignmentPreference(Literal literal, float weight)
void ResetActivitiesToFollowBestPartialAssignment()
void Untrail(int target_trail_index)
void IncreaseNumVariables(int num_variables)
void ResetDecisionHeuristic()
SatDecisionPolicy(Model *model)
void UpdateVariableActivityIncrement()
void BeforeConflict(int trail_index)
static constexpr Polarity POLARITY_FALSE
::int32_t polarity_rephase_increment() const
::operations_research::sat::SatParameters_Polarity initial_polarity() const
static constexpr VariableOrder IN_RANDOM_ORDER
static constexpr VariableOrder IN_REVERSE_ORDER
static constexpr VariableOrder IN_ORDER
static constexpr Polarity POLARITY_TRUE
static constexpr Polarity POLARITY_RANDOM
const VariablesAssignment & Assignment() const
bool VariableIsAssigned(BooleanVariable var) const
void assign(size_type n, const value_type &val)
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