30#include "absl/functional/any_invocable.h"
31#include "absl/functional/bind_front.h"
32#include "absl/functional/function_ref.h"
33#include "absl/log/check.h"
34#include "absl/random/bit_gen_ref.h"
35#include "absl/random/distributions.h"
36#include "absl/strings/str_cat.h"
37#include "absl/types/span.h"
41#include "ortools/sat/cp_model.pb.h"
46#include "ortools/sat/sat_parameters.pb.h"
57constexpr double kCompoundDiscount = 1. / 1024;
61 absl::AnyInvocable<std::pair<int64_t, double>(
int)
const> compute_jump) {
62 compute_jump_ = std::move(compute_jump);
66 deltas_.resize(num_variables);
67 scores_.resize(num_variables);
68 needs_recomputation_.assign(num_variables,
true);
74 needs_recomputation_[
var] =
false;
80 const auto& [
delta, score] = compute_jump_(
var);
82 LOG(ERROR) <<
"Incorrect delta for var " <<
var <<
": " << deltas_[
var]
83 <<
" (should be " <<
delta <<
")";
86 if (abs(score - scores_[
var]) / std::max(abs(score), 1.0) > 1e-2) {
88 LOG(ERROR) <<
"Incorrect score for var " <<
var <<
": " << scores_[
var]
89 <<
" (should be " << score <<
") " <<
" delta = " <<
delta;
91 return delta == deltas_[
var] && score_ok;
95 if (needs_recomputation_[
var]) {
96 needs_recomputation_[
var] =
false;
97 std::tie(deltas_[
var], scores_[
var]) = compute_jump_(
var);
99 return std::make_pair(deltas_[
var], scores_[
var]);
104 for (
int i = 0;
i < states_.size(); ++
i) {
109 for (
const auto& [options, counters] : options_to_stats_) {
111 absl::StrCat(name_,
"_", options.name()), counters.num_batches,
112 options_to_num_restarts_[options] + counters.num_perturbations,
113 counters.num_linear_moves, counters.num_general_moves,
114 counters.num_compound_moves, counters.num_backtracks,
115 counters.num_weight_updates, counters.num_scores_computed);
123void FeasibilityJumpSolver::ImportState() {
124 state_ = states_->GetNextState();
125 if (state_->
move ==
nullptr) {
126 const int num_variables = var_domains_.
size();
127 state_->
move = std::make_unique<CompoundMoveBuilder>(num_variables);
131void FeasibilityJumpSolver::ReleaseState() { states_->Release(state_); }
133void FeasibilityJumpSolver::Initialize() {
134 is_initialized_ =
true;
138 if (params_.feasibility_jump_linearization_level() == 0) {
140 std::make_unique<LsEvaluator>(linear_model_->
model_proto(), params_);
143 std::make_unique<LsEvaluator>(linear_model_->
model_proto(), params_,
148 const int num_variables = linear_model_->
model_proto().variables().size();
149 var_domains_.
resize(num_variables);
150 for (
int v = 0; v < num_variables; ++v) {
157 var_occurs_in_non_linear_constraint_.resize(num_variables);
158 for (
int c = 0;
c < evaluator_->NumNonLinearConstraints(); ++
c) {
159 for (
int v : evaluator_->GeneralConstraintToVars(c)) {
160 var_occurs_in_non_linear_constraint_[v] =
true;
167int64_t ComputeRange(int64_t
range,
double range_ratio) {
168 return static_cast<int64_t
>(
169 std::ceil(
static_cast<double>(
range) * range_ratio));
174int64_t RandomValueNearMin(
const Domain& domain,
double range_ratio,
175 absl::BitGenRef random) {
176 if (domain.Size() == 1)
return domain.FixedValue();
177 if (domain.Size() == 2) {
178 return absl::Bernoulli(random, 1 - range_ratio) ? domain.Min()
181 const int64_t
range = ComputeRange(domain.Max() - domain.Min(), range_ratio);
182 return domain.ValueAtOrBefore(domain.Min() +
183 absl::LogUniform<int64_t>(random, 0,
range));
186int64_t RandomValueNearMax(
const Domain& domain,
double range_ratio,
187 absl::BitGenRef random) {
188 if (domain.Size() == 1)
return domain.FixedValue();
189 if (domain.Size() == 2) {
190 return absl::Bernoulli(random, 1 - range_ratio) ? domain.Max()
193 const int64_t
range = ComputeRange(domain.Max() - domain.Min(), range_ratio);
194 return domain.ValueAtOrAfter(domain.Max() -
195 absl::LogUniform<int64_t>(random, 0,
range));
198int64_t RandomValueNearValue(
const Domain& domain, int64_t
value,
199 double range_ratio, absl::BitGenRef random) {
200 DCHECK(!domain.IsFixed());
202 if (domain.Min() >=
value) {
203 return RandomValueNearMin(domain, range_ratio, random);
206 if (domain.Max() <=
value) {
207 return RandomValueNearMax(domain, range_ratio, random);
211 const Domain greater_domain =
212 domain.IntersectionWith({
value + 1, domain.Max()});
213 const double choose_greater_probability =
214 static_cast<double>(greater_domain.Size()) /
215 static_cast<double>(domain.Size() - 1);
216 if (absl::Bernoulli(random, choose_greater_probability)) {
217 return RandomValueNearMin(greater_domain, range_ratio, random);
219 return RandomValueNearMax(
220 domain.IntersectionWith({domain.Min(), value - 1}), range_ratio,
227void FeasibilityJumpSolver::ResetCurrentSolution(
228 bool use_hint,
bool use_objective,
double perturbation_probability) {
229 const int num_variables = linear_model_->
model_proto().variables().size();
230 const double default_value_probability = 1.0 - perturbation_probability;
231 const double range_ratio =
232 params_.feasibility_jump_var_perburbation_range_ratio();
239 for (
int var = 0;
var < num_variables; ++
var) {
245 if (absl::Bernoulli(random_, default_value_probability)) {
249 RandomValueNearValue(var_domains_[
var], 0, range_ratio, random_);
254 if (use_objective && linear_model_->
model_proto().has_objective()) {
255 const int num_terms =
256 linear_model_->
model_proto().objective().vars().size();
257 for (
int i = 0;
i < num_terms; ++
i) {
261 if (linear_model_->
model_proto().objective().coeffs(
i) > 0) {
262 if (absl::Bernoulli(random_, default_value_probability)) {
266 RandomValueNearMin(var_domains_[
var], range_ratio, random_);
269 if (absl::Bernoulli(random_, default_value_probability)) {
273 RandomValueNearMax(var_domains_[
var], range_ratio, random_);
279 if (use_hint && linear_model_->
model_proto().has_solution_hint()) {
287void FeasibilityJumpSolver::PerturbateCurrentSolution(
288 double perturbation_probability) {
289 if (perturbation_probability == 0.0)
return;
290 const int num_variables = linear_model_->
model_proto().variables().size();
291 const double perturbation_ratio =
292 params_.feasibility_jump_var_perburbation_range_ratio();
294 for (
int var = 0;
var < num_variables; ++
var) {
296 if (absl::Bernoulli(random_, perturbation_probability)) {
298 perturbation_ratio, random_);
303std::string FeasibilityJumpSolver::OneLineStats()
const {
305 const std::string general_str =
313 const std::string compound_str =
317 : absl::StrCat(
" comp{mvs:",
323 const int num_infeasible_cts = evaluator_->NumInfeasibleConstraints();
324 const std::string non_solution_str =
325 num_infeasible_cts == 0
335 general_str, compound_str, non_solution_str,
341 task_generated_ =
true;
346 if (!is_initialized_) Initialize();
353 bool new_best_solution_was_found =
false;
357 if (best < state_->last_solution_rank) {
358 states_->ResetLubyCounter();
359 new_best_solution_was_found =
true;
364 bool reset_weights =
false;
366 reset_weights =
true;
368 states_->CollectStatistics(*state_);
382 new_best_solution_was_found) {
391 ResetCurrentSolution(first_time,
396 PerturbateCurrentSolution(
397 params_.feasibility_jump_var_randomization_probability());
402 states_->ConfigureNextLubyRestart(state_);
407 params_.violation_ls_perturbation_period();
412 bool recompute_compound_weights =
false;
413 if (linear_model_->
model_proto().has_objective()) {
418 recompute_compound_weights =
true;
424 if (evaluator_->ReduceObjectiveBounds(lb.value(), ub.value())) {
425 recompute_compound_weights =
true;
436 const int num_vars = state_->
solution.size();
437 for (
int var = 0;
var < num_vars; ++
var) {
439 const int64_t new_value = var_domains_[
var].ClosestValue(old_value);
440 if (new_value != old_value) {
442 recompute_compound_weights =
true;
447 if (!state_->
move->StackValuesInDomains(var_domains_.
AsSpan())) {
448 recompute_compound_weights =
true;
455 evaluator_->ComputeAllViolations(state_->
solution);
459 state_->
weights.assign(evaluator_->NumEvaluatorConstraints(), 1.0);
460 recompute_compound_weights =
true;
462 if (recompute_compound_weights) {
463 state_->
move->Clear();
467 for (
int c = 0; c < state_->
weights.size(); ++c) {
468 if (evaluator_->IsViolated(c))
continue;
479 DCHECK_EQ(state_->
move->Size(), 0);
483 if (DoSomeLinearIterations() && DoSomeGeneralIterations()) {
488 "(", OneLineStats(),
")"));
490 shared_response_->
LogMessage(
name(),
"infeasible solution. Aborting.");
491 model_is_supported_ =
false;
501 const double current_dtime = DeterministicTime();
514 task_generated_ =
false;
518double FeasibilityJumpSolver::ComputeScore(absl::Span<const double> weights,
522 double score = evaluator_->WeightedViolationDelta(
524 constexpr double kEpsilon = 1.0 / std::numeric_limits<int64_t>::max();
525 score += kEpsilon *
delta * evaluator_->ObjectiveCoefficient(
var);
529std::pair<int64_t, double> FeasibilityJumpSolver::ComputeLinearJump(
int var) {
531 const int64_t current_value = state_->
solution[
var];
534 const LinearIncrementalEvaluator& linear_evaluator =
535 evaluator_->LinearEvaluator();
538 const int64_t min_value = var_domains_[
var].Min();
539 const int64_t max_value = var_domains_[
var].Max();
540 const int64_t
delta = current_value == min_value ? max_value - min_value
541 : min_value - max_value;
542 return std::make_pair(
551 const int64_t p1 = var_domains_[
var].ValueAtOrBefore(current_value - 1);
552 const int64_t p2 = var_domains_[
var].ValueAtOrAfter(current_value + 1);
554 std::pair<int64_t, double> best_jump;
555 const double v1 = var_domains_[
var].Contains(p1)
556 ? ComputeScore(ScanWeights(),
var, p1 - current_value,
558 :
std::numeric_limits<double>::infinity();
563 const Domain dom = var_domains_[
var].IntersectionWith(
564 Domain(std::numeric_limits<int64_t>::min(), p1 - 1));
566 best_jump = {p1, v1};
569 linear_evaluator.SlopeBreakpoints(
var, current_value, dom);
571 true, {p1, v1}, tmp_breakpoints_,
572 [
this,
var, current_value](int64_t jump_value) {
573 return ComputeScore(ScanWeights(),
var, jump_value - current_value,
578 const double v2 = var_domains_[
var].Contains(p2)
579 ? ComputeScore(ScanWeights(),
var, p2 - current_value,
581 :
std::numeric_limits<double>::infinity();
585 const Domain dom = var_domains_[
var].IntersectionWith(
586 Domain(p2 + 1, std::numeric_limits<int64_t>::max()));
588 best_jump = {p2, v2};
591 linear_evaluator.SlopeBreakpoints(
var, current_value, dom);
593 false, {p2, v2}, tmp_breakpoints_,
594 [
this,
var, current_value](int64_t jump_value) {
595 return ComputeScore(ScanWeights(),
var,
596 jump_value - current_value,
606 best_jump = {p1, v1};
608 best_jump = {p2, v2};
612 DCHECK_NE(best_jump.first, current_value);
613 return std::make_pair(best_jump.first - current_value, best_jump.second);
616std::pair<int64_t, double> FeasibilityJumpSolver::ComputeGeneralJump(
int var) {
617 if (!var_occurs_in_non_linear_constraint_[
var]) {
618 return ComputeLinearJump(
var);
620 Domain domain = var_domains_[
var];
621 if (domain.IsFixed())
return std::make_pair(0, 0.0);
624 const int64_t current_value = state_->
solution[
var];
625 domain = domain.IntersectionWith(
626 Domain(current_value, current_value).Complement());
627 std::pair<int64_t, double> result;
628 for (
int i = 0;
i < domain.NumIntervals(); ++
i) {
629 const int64_t min_delta = domain[
i].start - current_value;
630 const int64_t max_delta = domain[
i].end - current_value;
632 min_delta, max_delta + 1, [&](int64_t
delta) ->
double {
633 return ComputeScore(ScanWeights(),
var,
delta,
false);
635 if (
i == 0 || score < result.second) {
636 result = std::make_pair(
delta, score);
639 DCHECK(domain.Contains(current_value + result.first))
640 << current_value <<
"+" << result.first <<
" not in domain "
641 << domain.ToString();
645void FeasibilityJumpSolver::UpdateViolatedConstraintWeights() {
652 const double kMaxWeight = 1e10;
653 const double kBumpFactor = 1.0 / params_.feasibility_jump_decay();
654 const int num_variables = var_domains_.
size();
661 bool rescale =
false;
662 num_ops_ += evaluator_->ViolatedConstraints().size();
663 for (
const int c : evaluator_->ViolatedConstraints()) {
664 DCHECK(evaluator_->IsViolated(c));
672 if (state_->
weights[c] > kMaxWeight) {
678 const double factor = 1.0 / kMaxWeight;
680 for (
int c = 0;
c < state_->
weights.size(); ++
c) {
696 LinearIncrementalEvaluator* linear_evaluator =
697 evaluator_->MutableLinearEvaluator();
698 linear_evaluator->ClearAffectedVariables();
699 for_weight_update_.resize(num_variables);
700 num_ops_ += evaluator_->ViolatedConstraints().size();
701 for (
const int c : evaluator_->ViolatedConstraints()) {
702 if (c < evaluator_->NumLinearConstraints()) {
703 linear_evaluator->UpdateScoreOnWeightUpdate(
704 c, jumps_.
Deltas(), absl::MakeSpan(for_weight_update_));
706 for (
const int v : evaluator_->ConstraintToVars(c)) {
716 for (
const int var : linear_evaluator->VariablesAffectedByLastUpdate()) {
732bool FeasibilityJumpSolver::DoSomeLinearIterations() {
741 evaluator_->RecomputeViolatedList(
true);
743 absl::bind_front(&FeasibilityJumpSolver::ComputeLinearJump,
this));
744 RecomputeVarsToScan();
748 const double dtime_threshold =
749 DeterministicTime() + params_.feasibility_jump_batch_dtime();
750 while (DeterministicTime() < dtime_threshold) {
752 while (DeterministicTime() < dtime_threshold) {
758 if (!ScanRelevantVariables(5, &best_var, &best_value,
765 const int64_t prev_value = state_->
solution[best_var];
766 state_->
solution[best_var] = best_value;
767 evaluator_->UpdateLinearScores(best_var, prev_value, best_value,
770 evaluator_->UpdateViolatedList();
773 MarkJumpsThatNeedToBeRecomputed(best_var);
777 jumps_.
SetJump(best_var, prev_value - best_value, -best_score);
780 if (time_limit_crossed_)
return false;
783 if (vars_to_scan_.
empty()) {
785 if (evaluator_->ViolatedConstraints().empty())
return true;
786 UpdateViolatedConstraintWeights();
805void FeasibilityJumpSolver::MarkJumpsThatNeedToBeRecomputed(
int changed_var) {
814 num_ops_ += evaluator_->VarToGeneralConstraints(changed_var).size();
815 for (
const int c : evaluator_->VarToGeneralConstraints(changed_var)) {
816 num_ops_ += evaluator_->GeneralConstraintToVars(c).size();
817 for (
const int var : evaluator_->GeneralConstraintToVars(c)) {
824 num_ops_ += evaluator_->VariablesAffectedByLastLinearUpdate().size();
825 for (
const int var : evaluator_->VariablesAffectedByLastLinearUpdate()) {
833bool FeasibilityJumpSolver::DoSomeGeneralIterations() {
835 evaluator_->NumNonLinearConstraints() == 0) {
839 evaluator_->ComputeAllNonLinearViolations(state_->
solution);
840 evaluator_->RecomputeViolatedList(
false);
841 if (evaluator_->NumNonLinearConstraints() == 0) {
843 absl::bind_front(&FeasibilityJumpSolver::ComputeLinearJump,
this));
846 absl::bind_front(&FeasibilityJumpSolver::ComputeGeneralJump,
this));
848 RecomputeVarsToScan();
850 const double dtime_threshold =
851 DeterministicTime() + params_.feasibility_jump_batch_dtime();
852 while (DeterministicTime() < dtime_threshold) {
856 const bool found_move = ScanRelevantVariables(
857 3, &
var, &new_value, &score);
858 const bool backtrack =
859 !found_move && state_->
move->Backtrack(&
var, &new_value, &score);
860 if (found_move || backtrack) {
862 DCHECK_NE(
var, -1) <<
var <<
" " << found_move <<
" " << backtrack;
867 DCHECK_NE(prev_value, new_value);
871 evaluator_->UpdateLinearScores(
var, prev_value, new_value, ScanWeights(),
873 evaluator_->UpdateNonLinearViolations(
var, prev_value, state_->
solution);
874 evaluator_->UpdateViolatedList();
881 for (
const auto& c : evaluator_->last_update_violation_changes()) {
882 if (evaluator_->IsViolated(c) &&
889 for (
const int v : evaluator_->ConstraintToVars(c)) {
893 }
else if (!evaluator_->IsViolated(c) &&
907 DCHECK_EQ(-score, ComputeScore(state_->
weights,
var,
908 prev_value - new_value,
false));
909 DCHECK_EQ(-score, ComputeScore(ScanWeights(),
var,
910 prev_value - new_value,
false));
913 MarkJumpsThatNeedToBeRecomputed(
var);
917 jumps_.
SetJump(
var, prev_value - new_value, -score);
923 state_->
move->Push(
var, prev_value, score);
924 if (state_->
move->Score() < 0) {
926 state_->
move->Clear();
931 }
else if (time_limit_crossed_) {
935 DCHECK_EQ(state_->
move->Size(), 0);
936 if (evaluator_->ViolatedConstraints().empty())
return true;
938 ResetChangedCompoundWeights();
943 UpdateViolatedConstraintWeights();
949void FeasibilityJumpSolver::ResetChangedCompoundWeights() {
951 DCHECK_EQ(state_->
move->Size(), 0);
955 const double expected_weight =
956 (evaluator_->IsViolated(c) ? 1.0 : kCompoundDiscount) *
960 num_ops_ += evaluator_->ConstraintToVars(c).size();
961 for (
const int var : evaluator_->ConstraintToVars(c)) {
969bool FeasibilityJumpSolver::ShouldExtendCompoundMove(
double score,
971 if (state_->
move->Score() + score - std::max(novelty, 0.0) < 0) {
974 return score < state_->
move->BestChildScore();
977bool FeasibilityJumpSolver::ScanRelevantVariables(
int num_to_scan,
980 double* best_score) {
981 if (time_limit_crossed_)
return false;
985 double best_scan_score = 0.0;
991 auto remove_var_to_scan_at_index = [&](
int index) {
992 in_vars_to_scan_[vars_to_scan_[
index]] =
false;
993 vars_to_scan_[
index] = vars_to_scan_.back();
994 vars_to_scan_.pop_back();
995 if (best_index == vars_to_scan_.size()) {
999 while (!vars_to_scan_.empty() && num_good < num_to_scan) {
1001 const int index = absl::Uniform<int>(random_, 0, vars_to_scan_.size());
1002 const int var = vars_to_scan_[
index];
1004 DCHECK(in_vars_to_scan_[
var]);
1006 if (!ShouldScan(
var)) {
1007 remove_var_to_scan_at_index(
index);
1015 shared_time_limit_ !=
nullptr && shared_time_limit_->
LimitReached()) {
1016 time_limit_crossed_ =
true;
1019 const int64_t current_value = state_->
solution[
var];
1020 DCHECK(var_domains_[
var].Contains(current_value +
delta))
1021 <<
var <<
" " << current_value <<
"+" <<
delta <<
" not in "
1022 << var_domains_[
var].ToString();
1024 if (scan_score >= 0) {
1025 remove_var_to_scan_at_index(
index);
1028 double score = scan_score;
1031 score = ComputeScore(
1033 !var_occurs_in_non_linear_constraint_[
var]);
1034 if (!ShouldExtendCompoundMove(score, score - scan_score)) {
1035 remove_var_to_scan_at_index(
index);
1041 if (scan_score < best_scan_score) {
1042 DCHECK_NE(
delta, 0) << score;
1044 *best_value = current_value +
delta;
1045 *best_score = score;
1046 best_scan_score = scan_score;
1050 if (best_index != -1) {
1051 DCHECK_GT(num_good, 0);
1052 DCHECK_GE(*best_var, 0);
1053 DCHECK_EQ(*best_var, vars_to_scan_[best_index]);
1054 remove_var_to_scan_at_index(best_index);
1060void FeasibilityJumpSolver::AddVarToScan(
int var) {
1062 if (in_vars_to_scan_[
var])
return;
1063 if (!ShouldScan(
var))
return;
1064 vars_to_scan_.push_back(
var);
1065 in_vars_to_scan_[
var] =
true;
1068bool FeasibilityJumpSolver::ShouldScan(
int var)
const {
1071 if (state_->
move->OnStack(
var))
return false;
1075 const double score = jumps_.
Score(
var);
1095 return evaluator_->NumViolatedConstraintsForVarIgnoringObjective(
var) > 0;
1098void FeasibilityJumpSolver::RecomputeVarsToScan() {
1099 const int num_variables = var_domains_.
size();
1101 DCHECK(SlowCheckNumViolatedConstraints());
1103 in_vars_to_scan_.assign(num_variables,
false);
1104 vars_to_scan_.clear();
1110 in_vars_to_scan_[
var] =
true;
1113 num_ops_ += evaluator_->ViolatedConstraints().size();
1114 for (
const int c : evaluator_->ViolatedConstraints()) {
1115 num_ops_ += evaluator_->ConstraintToVars(c).size();
1116 for (
const int v : evaluator_->ConstraintToVars(c)) {
1122bool FeasibilityJumpSolver::SlowCheckNumViolatedConstraints()
const {
1123 std::vector<int> result;
1124 result.assign(var_domains_.
size(), 0);
1125 for (
const int c : evaluator_->ViolatedConstraints()) {
1126 if (evaluator_->IsObjectiveConstraint(c))
continue;
1127 for (
const int v : evaluator_->ConstraintToVars(c)) {
1131 for (
int v = 0; v < result.size(); ++v) {
1133 evaluator_->NumViolatedConstraintsForVarIgnoringObjective(v));
1139 for (
const UnitMove& move : stack_) {
1140 var_on_stack_[move.var] =
false;
1146 return !stack_.empty() && var_on_stack_[
var];
1150 if (stack_.empty())
return false;
1151 *
var = stack_.back().var;
1152 *
value = stack_.back().prev_value;
1153 *score = stack_.back().score;
1154 var_on_stack_[*
var] =
false;
1156 if (!stack_.empty()) {
1157 ++stack_.back().discrepancy;
1163 DCHECK(!var_on_stack_[
var]);
1164 if (!stack_.empty()) {
1165 stack_.back().best_child_score =
1166 std::min(stack_.back().best_child_score, score);
1168 var_on_stack_[
var] =
true;
1171 .prev_value = prev_value,
1173 .cumulative_score =
Score() + score,
1179 absl::Span<const Domain> var_domains)
const {
1180 for (
const UnitMove& move : stack_) {
1181 if (!var_domains[move.var].Contains(move.prev_value))
return false;
void AdvanceDeterministicTime(double deterministic_duration)
bool LimitReached() const
bool StackValuesInDomains(absl::Span< const Domain > var_domains) const
Returns true if all prev_values on the stack are in the appropriate domain.
double Score() const
Returns the sum of scores of atomic moved pushed to this compound move.
void Clear()
Removes all moves on the stack.
int Discrepancy() const
Returns the number of backtracks to any ancestor of the current leaf.
bool OnStack(int var) const
Returns true if this var has been set in this move already,.
bool Backtrack(int *var, int64_t *value, double *score)
void Push(int var, int64_t prev_value, double score)
~FeasibilityJumpSolver() override
If VLOG_IS_ON(1), it will export a bunch of statistics.
std::function< void()> GenerateTask(int64_t) final
void ClearAndReserve(size_t size)
void Recompute(int var)
Recompute the jump for var when GetJump(var) is next called.
void RecomputeAll(int num_variables)
std::pair< int64_t, double > GetJump(int var)
Gets the current jump delta and score, recomputing if necessary.
absl::Span< const int64_t > Deltas() const
double Score(int var) const
absl::Span< double > MutableScores()
void SetComputeFunction(absl::AnyInvocable< std::pair< int64_t, double >(int) const > compute_jump)
bool NeedRecomputation(int var) const
bool JumpIsUpToDate(int var) const
void SetJump(int var, int64_t delta, double score)
const CpModelProto & model_proto() const
const std::vector< bool > & ignored_constraints() const
Mask on the constraints of the model passed to the ctor.
const std::vector< ConstraintProto > & additional_constraints() const
Additional constraints created during the initialization.
void CollectStatistics(const LsState &state)
Accumulate in the relevant bucket the counters of the given states.
IntegerValue GetInnerObjectiveLowerBound()
void LogMessageWithThrottling(const std::string &prefix, const std::string &message)
void LogMessage(absl::string_view prefix, absl::string_view message)
Wrapper around our SolverLogger, but protected by mutex.
IntegerValue GetInnerObjectiveUpperBound()
void NewSolution(absl::Span< const int64_t > solution_values, const std::string &solution_info, Model *model=nullptr)
const SharedSolutionRepository< int64_t > & SolutionsRepository() const
Solution GetRandomBiasedSolution(absl::BitGenRef random) const
Returns a random solution biased towards good solutions.
int64_t GetBestRank() const
void AddTimingStat(const SubSolver &subsolver)
Add a line to the corresponding table.
void AddLsStat(absl::string_view name, int64_t num_batches, int64_t num_restarts, int64_t num_linear_moves, int64_t num_general_moves, int64_t num_compound_moves, int64_t num_bactracks, int64_t num_weight_updates, int64_t num_scores_computed)
void AddTaskDeterministicDuration(double deterministic_duration)
SubsolverType type() const
Returns the type of the subsolver.
double deterministic_time() const
std::string name() const
Returns the name of this SubSolver. Used in logs.
bool HasBetterObjectiveValue(int var) const
bool UpdateFromSharedBounds()
void resize(int num_vars)
void Set(int var, Domain d)
bool HasTwoValues(int var) const
void InitializeObjective(const CpModelProto &cp_model_proto)
absl::Span< const Domain > AsSpan() const
bool IsFixed(int var) const
void OnValueChange(int var, int64_t value)
Tricky: this must be called on solution value change or domains update.
absl::Span< const int > FixedVariables() const
std::optional< ModelSolveParameters::SolutionHint > hint
bool SolutionIsFeasible(const CpModelProto &model, absl::Span< const int64_t > variable_values, const CpModelProto *mapping_proto, const std::vector< int > *postsolve_mapping)
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
std::string FormatCounter(int64_t num)
Prints a positive number with separators for easier reading (ex: 1'348'065).
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a proto.
std::pair< Point, Value > RangeConvexMinimum(Point begin, Point end, absl::FunctionRef< Value(Point)> f)
Searches in the range [begin, end), where Point supports basic arithmetic.
std::pair< Point, Value > ConvexMinimum(absl::Span< const Point > sorted_points, std::function< Value(Point)> f)
const std::optional< Range > & range
int64_t num_perturbations
int64_t num_weight_updates
int64_t num_compound_moves
int64_t num_general_moves
int64_t num_scores_computed
int64_t num_general_evals
void Randomize(const SatParameters ¶ms, ModelRandomGenerator *random)
double perturbation_probability
These are randomized each restart by Randomize().
std::string name() const
Allows to identify which options worked well.
std::vector< double > compound_weights
std::vector< int64_t > solution
IntegerValue saved_inner_objective_lb
LsCounters counters
Counters for a "non-restarted" run.
IntegerValue saved_inner_objective_ub
int64_t num_solutions_imported
int compound_move_max_discrepancy
std::vector< double > weights
int64_t last_solution_rank
Used by LS to know the rank of the starting solution for this state.
LsOptions options
Strategy.
std::unique_ptr< CompoundMoveBuilder > move
int64_t num_batches_before_change
When this reach zero, we restart / perturbate or trigger something.
int64_t num_restarts
Global counters, incremented across restart.
std::vector< int > compound_weight_changed
std::vector< bool > in_compound_weight_changed
The solution format used by this class.