Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
feasibility_jump.h
Go to the documentation of this file.
1// Copyright 2010-2024 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#ifndef OR_TOOLS_SAT_FEASIBILITY_JUMP_H_
15#define OR_TOOLS_SAT_FEASIBILITY_JUMP_H_
16
17#include <algorithm>
18#include <atomic>
19#include <cstddef>
20#include <cstdint>
21#include <functional>
22#include <limits>
23#include <memory>
24#include <string>
25#include <utility>
26#include <vector>
27
28#include "absl/container/flat_hash_map.h"
29#include "absl/functional/any_invocable.h"
30#include "absl/functional/bind_front.h"
31#include "absl/hash/hash.h"
32#include "absl/log/check.h"
33#include "absl/random/distributions.h"
34#include "absl/strings/str_join.h"
35#include "absl/strings/string_view.h"
36#include "absl/synchronization/mutex.h"
37#include "absl/types/span.h"
40#include "ortools/sat/integer.h"
42#include "ortools/sat/restart.h"
43#include "ortools/sat/sat_parameters.pb.h"
47#include "ortools/sat/util.h"
49
51
52class CompoundMoveBuilder;
53
54// This class lazily caches the results of `compute_jump(var)` which returns a
55// <delta, score> pair.
56// Variables' scores can be manually modified using MutableScores (if the
57// optimal jump is known not to change), or marked for recomputation on the next
58// call to GetJump(var) by calling Recompute.
59class JumpTable {
60 public:
61 JumpTable() = default;
62
64 absl::AnyInvocable<std::pair<int64_t, double>(int) const> compute_jump);
65
66 void RecomputeAll(int num_variables);
67
68 // Gets the current jump delta and score, recomputing if necessary.
69 std::pair<int64_t, double> GetJump(int var);
70
71 // If the new optimum value and score is known, users can update it directly.
72 // e.g. after weight rescaling, or after changing a binary variable.
73 void SetJump(int var, int64_t delta, double score);
74
75 // Recompute the jump for `var` when `GetJump(var)` is next called.
76 void Recompute(int var);
77
78 bool NeedRecomputation(int var) const { return needs_recomputation_[var]; }
79
80 double Score(int var) const { return scores_[var]; }
81
82 // Advanced usage, allows users to read possibly stale deltas for incremental
83 // score updates.
84 absl::Span<const int64_t> Deltas() const {
85 return absl::MakeConstSpan(deltas_);
86 }
87 absl::Span<const double> Scores() const {
88 return absl::MakeConstSpan(scores_);
89 }
90
91 absl::Span<double> MutableScores() { return absl::MakeSpan(scores_); }
92
93 // For debugging and testing.
94 //
95 // Note if you have very high weights (e.g. when using decay), the tolerances
96 // in this function are likely too tight.
97 bool JumpIsUpToDate(int var) const;
98
99 private:
100 absl::AnyInvocable<std::pair<int64_t, double>(int) const> compute_jump_;
101
102 // For each variable, we store:
103 // - A jump delta which represents a change in variable value:
104 // new_value = old + delta, which is non-zero except for fixed variables.
105 // - The associated weighted feasibility violation change if we take this
106 // jump.
107 std::vector<int64_t> deltas_;
108 std::vector<double> scores_;
109 std::vector<bool> needs_recomputation_;
110};
111
112// Accessing Domain can be expensive, so we maintain vector of bool for the
113// hot spots.
115 public:
116 explicit VarDomainWrapper(SharedBoundsManager* shared_bounds)
117 : shared_bounds_id_(
118 shared_bounds == nullptr ? 0 : shared_bounds->RegisterNewId()),
119 shared_bounds_(shared_bounds) {}
120
121 Domain operator[](int var) const { return domains_[var]; }
122 bool HasTwoValues(int var) const { return has_two_values_[var]; }
123 size_t size() const { return domains_.size(); }
124
125 void resize(int num_vars) {
126 domains_.resize(num_vars);
127 has_two_values_.resize(num_vars);
128 is_fixed_.resize(num_vars, false);
129 objective_is_positive_.resize(num_vars, false);
130 objective_is_negative_.resize(num_vars, false);
131 has_better_objective_value_.resize(num_vars, false);
132 }
133
134 void Set(int var, Domain d) {
135 has_two_values_[var] = d.HasTwoValues();
136 if (is_fixed_[var]) {
137 // The code here assume that once fixed, a variable stays that way.
138 CHECK(d.IsFixed());
139 } else if (d.IsFixed()) {
140 is_fixed_[var] = true;
141 fixed_vars_.push_back(var);
142 }
143 domains_[var] = std::move(d);
144 }
145
146 // Return false if one of the domain becomes empty (UNSAT). This might happen
147 // while we are cleaning up all workers at the end of a search.
149 if (shared_bounds_ == nullptr) return true;
150 shared_bounds_->GetChangedBounds(shared_bounds_id_, &tmp_variables_,
151 &tmp_new_lower_bounds_,
152 &tmp_new_upper_bounds_);
153 for (int i = 0; i < tmp_variables_.size(); ++i) {
154 const int var = tmp_variables_[i];
155 const Domain new_domain = domains_[var].IntersectionWith(
156 Domain(tmp_new_lower_bounds_[i], tmp_new_upper_bounds_[i]));
157 if (new_domain.IsEmpty()) return false;
158 Set(var, new_domain);
159 }
160 return true;
161 }
162
163 absl::Span<const Domain> AsSpan() const { return domains_; }
164
165 void InitializeObjective(const CpModelProto& cp_model_proto) {
166 if (!cp_model_proto.has_objective()) return;
167 const int num_terms = cp_model_proto.objective().vars().size();
168 for (int i = 0; i < num_terms; ++i) {
169 const int var = cp_model_proto.objective().vars(i);
170 const int coeff = cp_model_proto.objective().coeffs(i);
171 objective_is_positive_[var] = coeff > 0;
172 objective_is_negative_[var] = coeff < 0;
173 }
174 }
175
176 bool IsFixed(int var) const { return is_fixed_[var]; }
177
178 bool HasBetterObjectiveValue(int var) const {
179 return has_better_objective_value_[var];
180 }
181
182 // Tricky: this must be called on solution value change or domains update.
183 void OnValueChange(int var, int64_t value) {
184 has_better_objective_value_[var] =
185 (objective_is_positive_[var] && value > domains_[var].Min()) ||
186 (objective_is_negative_[var] && value < domains_[var].Max());
187 }
188
189 absl::Span<const int> FixedVariables() const { return fixed_vars_; }
190
191 private:
192 const int shared_bounds_id_;
193 SharedBoundsManager* shared_bounds_;
194
195 // Basically fixed once and for all.
196 std::vector<bool> objective_is_positive_;
197 std::vector<bool> objective_is_negative_;
198
199 // Depends on domain updates.
200 std::vector<Domain> domains_;
201 std::vector<bool> has_two_values_;
202 std::vector<bool> is_fixed_;
203 std::vector<int> fixed_vars_;
204
205 // This is the only one that depends on the current solution value.
206 std::vector<bool> has_better_objective_value_;
207
208 // Temporary data for UpdateFromSharedBounds()
209 std::vector<int> tmp_variables_;
210 std::vector<int64_t> tmp_new_lower_bounds_;
211 std::vector<int64_t> tmp_new_upper_bounds_;
212};
213
214// Local search counters. This can either be the stats of one run without
215// restart or some aggregation of such runs.
241
242// The parameters used by the local search code.
243struct LsOptions {
244 // This one never changes.
245 // - If true, each restart is independent from the other. This is nice because
246 // it plays well with the theoretical Luby restart sequence.
247 // - If false, we always "restart" from the current state, but we perturb it
248 // or just reset the constraint weight. We currently use this one way less
249 // often.
250 bool use_restart = true;
251
252 // These are randomized each restart by Randomize().
254 bool use_decay = true;
256 bool use_objective = true; // No effect if there are no objective.
257
258 // Allows to identify which options worked well.
259 std::string name() const {
260 std::vector<absl::string_view> parts;
261 parts.reserve(5);
262 if (use_restart) parts.push_back("restart");
263 if (use_decay) parts.push_back("decay");
264 if (use_compound_moves) parts.push_back("compound");
265 if (perturbation_probability > 0) parts.push_back("perturb");
266 if (use_objective) parts.push_back("obj");
267 return absl::StrJoin(parts, "_");
268 }
269
270 // In order to collect statistics by options.
271 template <typename H>
272 friend H AbslHashValue(H h, const LsOptions& o) {
273 return H::combine(std::move(h), o.use_restart, o.perturbation_probability,
275 }
276
284
285 void Randomize(const SatParameters& params, ModelRandomGenerator* random) {
287 absl::Bernoulli(*random, 0.5)
288 ? 0.0
289 : params.feasibility_jump_var_randomization_probability();
290 use_decay = absl::Bernoulli(*random, 0.5);
291 use_compound_moves = absl::Bernoulli(*random, 0.5);
292 use_objective = absl::Bernoulli(*random, 0.5);
293 }
294};
295
296// Each FeasibilityJumpSolver work on many LsState in an interleaved parallel
297// fashion. Each "batch of moves" will update one of these state. Restart
298// heuristic are also on a per state basis.
299//
300// This allows to not use O(problem size) per state while having a more
301// diverse set of heuristics.
302struct LsState {
303 // The score of a solution is just the sum of infeasibility of each
304 // constraint weighted by these weights.
305 std::vector<int64_t> solution;
306 std::vector<double> weights;
307
308 // Depending on the options, we use an exponentially decaying constraint
309 // weight like for SAT activities.
310 double bump_value = 1.0;
311
312 // If using compound moves, these will be discounted on a new incumbent then
313 // re-converge to weights after some exploration. Search will repeatedly pick
314 // moves with negative WeightedViolationDelta using these weights.
315 //
316 // We limit the discrepancy in compound move search (i.e. limit the number of
317 // backtracks to any ancestor of the current leaf). This is set to 0 whenever
318 // a new incumbent is found or weights are updated, and increased at fixed
319 // point. Weights are only increased if no moves are found with discrepancy 2.
320 // Empirically we have seen very few moves applied with discrepancy > 2.
322 std::vector<double> compound_weights;
323 std::vector<bool> in_compound_weight_changed;
324 std::vector<int> compound_weight_changed;
325 std::unique_ptr<CompoundMoveBuilder> move;
326
327 // Counters for a "non-restarted" run.
329
330 // Strategy
332
333 // Global counters, incremented across restart.
334 int64_t num_restarts = 0;
336
337 // When this reach zero, we restart / perturbate or trigger something.
339
340 // Used by LS to know the rank of the starting solution for this state.
341 int64_t last_solution_rank = std::numeric_limits<int64_t>::max();
342
343 // Tricky: If this changed since last time, we need to recompute the
344 // compound moves as the objective constraint bound changed.
345 IntegerValue saved_inner_objective_lb = 0;
346 IntegerValue saved_inner_objective_ub = 0;
347};
348
349// Shared set of local search states that we work on.
351 public:
352 // Important: max_parallelism should be greater or equal than the actual
353 // number of thread sharing this class, otherwise the code will break.
354 SharedLsStates(absl::string_view name, const SatParameters& params,
355 SharedStatTables* stat_tables)
356 : name_(name), params_(params), stat_tables_(stat_tables) {
357 // We always start with at least 8 states.
358 // We will create more if there are more parallel workers as needed.
359 for (int i = 0; i < 8; ++i) CreateNewState();
360 }
361
363
365 const int index = states_.size();
366 states_.emplace_back(new LsState());
367 taken_.push_back(false);
368 num_selected_.push_back(0);
369
370 // We add one no-restart per 16 states and put it last.
371 states_.back()->options.use_restart = (index % 16 != 15);
372 }
373
374 // Returns the next available state in round-robin fashion.
375 // This is thread safe. If we respect the max_parallelism guarantee, then
376 // all states should be independent.
378 absl::MutexLock mutex_lock(&mutex_);
379 int next = -1;
380 const int num_states = states_.size();
381 for (int i = 0; i < num_states; ++i) {
382 const int index = round_robin_index_;
383 round_robin_index_ = (round_robin_index_ + 1) % num_states;
384 if (taken_[index]) continue;
385 if (next == -1 || num_selected_[index] < num_selected_[next]) {
386 next = index;
387 }
388 }
389
390 if (next == -1) {
391 // We need more parallelism and create a new state.
392 next = num_states;
394 }
395
396 --states_[next]->num_batches_before_change;
397 taken_[next] = true;
398 num_selected_[next]++;
399 return states_[next].get();
400 }
401
402 void Release(LsState* state) {
403 absl::MutexLock mutex_lock(&mutex_);
404 for (int i = 0; i < states_.size(); ++i) {
405 if (state == states_[i].get()) {
406 taken_[i] = false;
407 break;
408 }
409 }
410 }
411
413 absl::MutexLock mutex_lock(&mutex_);
414 luby_counter_ = 0;
415 }
416
417 // We share a global running Luby sequence for all the "restart" state.
418 // Note that we randomize the parameters on each restart.
419 //
420 // Hack: options.use_restart is constant, so we are free to inspect it.
421 // Also if options.use_restart, then num_batches_before_change is only
422 // modified under lock, so this code should be thread safe.
424 absl::MutexLock mutex_lock(&mutex_);
425 const int factor = std::max(1, params_.feasibility_jump_restart_factor());
426 CHECK(state->options.use_restart);
427 const int64_t next = factor * SUniv(++luby_counter_);
429 }
430
431 // Accumulate in the relevant bucket the counters of the given states.
432 void CollectStatistics(const LsState& state) {
433 if (state.counters.num_batches == 0) return;
434
435 absl::MutexLock mutex_lock(&mutex_);
436 options_to_stats_[state.options].AddFrom(state.counters);
437 options_to_num_restarts_[state.options]++;
438 }
439
440 private:
441 const std::string name_;
442 const SatParameters& params_;
443 SharedStatTables* stat_tables_;
444
445 mutable absl::Mutex mutex_;
446 int round_robin_index_ = 0;
447 std::vector<std::unique_ptr<LsState>> states_;
448 std::vector<bool> taken_;
449 std::vector<int> num_selected_;
450 int luby_counter_ = 0;
451
452 absl::flat_hash_map<LsOptions, LsCounters> options_to_stats_;
453 absl::flat_hash_map<LsOptions, int> options_to_num_restarts_;
454};
455
456// Implements and heuristic similar to the one described in the paper:
457// "Feasibility Jump: an LP-free Lagrangian MIP heuristic", Bjørnar
458// Luteberget, Giorgio Sartor, 2023, Mathematical Programming Computation.
459//
460// This is basically a Guided local search (GLS) with a nice algo to know what
461// value an integer variable should move to (its jump value). For binary, it
462// can only be swapped, so the situation is easier.
463//
464// TODO(user): If we have more than one of these solver, we might want to share
465// the evaluator memory between them. Right now we basically keep a copy of the
466// model and its transpose for each FeasibilityJumpSolver.
468 public:
469 FeasibilityJumpSolver(const absl::string_view name,
471 const LinearModel* linear_model, SatParameters params,
472 std::shared_ptr<SharedLsStates> ls_states,
473 ModelSharedTimeLimit* shared_time_limit,
474 SharedResponseManager* shared_response,
475 SharedBoundsManager* shared_bounds,
476 SharedStatistics* shared_stats,
477 SharedStatTables* stat_tables)
478 : SubSolver(name, type),
479 linear_model_(linear_model),
480 params_(params),
481 states_(std::move(ls_states)),
482 shared_time_limit_(shared_time_limit),
483 shared_response_(shared_response),
484 stat_tables_(stat_tables),
485 random_(params_),
486 var_domains_(shared_bounds) {}
487
488 // If VLOG_IS_ON(1), it will export a bunch of statistics.
489 ~FeasibilityJumpSolver() override;
490
491 // No synchronization needed for TaskIsAvailable().
492 void Synchronize() final {}
493
494 // Shall we delete this subsolver?
495 bool IsDone() final {
496 if (!model_is_supported_.load()) return true;
497
498 // We are done after the first task is done in the FIRST_SOLUTION mode.
499 return type() == SubSolver::FIRST_SOLUTION &&
500 shared_response_->first_solution_solvers_should_stop()->load();
501 }
502
503 bool TaskIsAvailable() final {
504 if (IsDone()) return false;
505 if (task_generated_.load()) return false;
506 if (shared_response_->ProblemIsSolved()) return false;
507 if (shared_time_limit_->LimitReached()) return false;
508
509 return (shared_response_->SolutionsRepository().NumSolutions() > 0) ==
511 }
512
513 std::function<void()> GenerateTask(int64_t /*task_id*/) final;
514
515 private:
516 void ImportState();
517 void ReleaseState();
518
519 void Initialize();
520 void ResetCurrentSolution(bool use_hint, bool use_objective,
521 double perturbation_probability);
522 void PerturbateCurrentSolution(double perturbation_probability);
523 std::string OneLineStats() const;
524
525 absl::Span<const double> ScanWeights() const {
526 return absl::MakeConstSpan(state_->options.use_compound_moves
527 ? state_->compound_weights
528 : state_->weights);
529 }
530
531 // Returns the weighted violation delta plus epsilon * the objective delta.
532 double ComputeScore(absl::Span<const double> weights, int var, int64_t delta,
533 bool linear_only);
534
535 // Computes the optimal value for variable v, considering only the violation
536 // of linear constraints.
537 std::pair<int64_t, double> ComputeLinearJump(int var);
538
539 // Computes the optimal value for variable v, considering all constraints
540 // (assuming violation functions are convex).
541 std::pair<int64_t, double> ComputeGeneralJump(int var);
542
543 // Marks all variables whose jump value may have changed due to the last
544 // update, except for `changed var`.
545 void MarkJumpsThatNeedToBeRecomputed(int changed_var);
546
547 // Moves.
548 bool DoSomeLinearIterations();
549 bool DoSomeGeneralIterations();
550
551 // Returns true if an improving move was found.
552 bool ScanRelevantVariables(int num_to_scan, int* var, int64_t* value,
553 double* score);
554
555 // Increases the weight of the currently infeasible constraints.
556 // Ensures jumps remains consistent.
557 void UpdateViolatedConstraintWeights();
558
559 // Returns true if it is possible that `var` may have value that reduces
560 // weighted violation or improve the objective.
561 // Note that this is independent of the actual weights used.
562 bool ShouldScan(int var) const;
563
564 void AddVarToScan(int var);
565 void RecomputeVarsToScan();
566
567 // Resets the weights used to find compound moves.
568 // Ensures the following invariant holds afterwards:
569 // compound_weights[c] = weights_[c] if c is violated, and epsilon *
570 // weights_[c] otherwise.
571 void ResetChangedCompoundWeights();
572
573 // Returns true if we should push this change to move_.
574 // `novelty` is the total discount applied to the score due to using
575 // `cumulative_weights_`, should always be positive (modulo floating-point
576 // errors).
577 bool ShouldExtendCompoundMove(double score, double novelty);
578
579 // Validates each element in num_violated_constraints_per_var_ against
580 // evaluator_->ViolatedConstraints.
581 bool SlowCheckNumViolatedConstraints() const;
582
583 double DeterministicTime() const {
584 return evaluator_->DeterministicTime() + num_ops_ * 1e-8;
585 }
586
587 const LinearModel* linear_model_;
588 SatParameters params_;
589 std::shared_ptr<SharedLsStates> states_;
590 ModelSharedTimeLimit* shared_time_limit_;
591 SharedResponseManager* shared_response_;
592 SharedStatTables* stat_tables_;
593 ModelRandomGenerator random_;
594
595 VarDomainWrapper var_domains_;
596
597 // Synchronization Booleans.
598 //
599 // Note that we don't fully support all type of model, and we will abort by
600 // setting the model_is_supported_ bool to false when we detect this.
601 bool is_initialized_ = false;
602 std::atomic<bool> model_is_supported_ = true;
603 std::atomic<bool> task_generated_ = false;
604 bool time_limit_crossed_ = false;
605
606 std::unique_ptr<LsEvaluator> evaluator_;
607 std::vector<bool> var_occurs_in_non_linear_constraint_;
608
609 JumpTable jumps_;
610 std::vector<double> for_weight_update_;
611
612 // The current sate we work on.
613 LsState* state_;
614
615 // A list of variables that might be relevant to check for improving jumps.
616 std::vector<bool> in_vars_to_scan_;
617 FixedCapacityVector<int> vars_to_scan_;
618
619 std::vector<int64_t> tmp_breakpoints_;
620
621 // For counting the dtime. See DeterministicTime().
622 int64_t num_ops_ = 0;
623};
624
625// This class helps keep track of moves that change more than one variable.
626// Mainly this class keeps track of how to backtrack back to the local minimum
627// as you make a sequence of exploratory moves, so in order to commit a compound
628// move, you just need to call `Clear` instead of Backtracking over the changes.
630 public:
631 explicit CompoundMoveBuilder(int num_variables)
632 : var_on_stack_(num_variables, false) {}
633
634 // Adds an atomic move to the stack.
635 // `var` must not be on the stack (this is DCHECKed).
636 void Push(int var, int64_t prev_value, double score);
637
638 // Sets var, val and score to a move that will revert the most recent atomic
639 // move on the stack, and pops this move from the stack.
640 // Returns false if the stack is empty.
641 bool Backtrack(int* var, int64_t* value, double* score);
642
643 // Removes all moves on the stack.
644 void Clear();
645
646 // Returns the number of variables in the move.
647 int Size() const { return stack_.size(); }
648
649 // Returns true if this var has been set in this move already,
650 bool OnStack(int var) const;
651
652 // Returns the sum of scores of atomic moved pushed to this compound move.
653 double Score() const {
654 return stack_.empty() ? 0.0 : stack_.back().cumulative_score;
655 }
656
657 double BestChildScore() const {
658 return stack_.empty() ? 0.0 : stack_.back().best_child_score;
659 }
660
661 // Returns the number of backtracks to any ancestor of the current leaf.
662 int Discrepancy() const {
663 return stack_.empty() ? 0 : stack_.back().discrepancy;
664 }
665
666 // Returns true if all prev_values on the stack are in the appropriate domain.
667 bool StackValuesInDomains(absl::Span<const Domain> var_domains) const;
668
669 private:
670 struct UnitMove {
671 int var;
672 int64_t prev_value;
673 // Note that this stores the score of reverting to prev_value.
674 double score;
675 // Instead of tracking this on the compound move, we store the partial sums
676 // to avoid numerical issues causing negative scores after backtracking.
677 double cumulative_score;
678
679 // Used to avoid infinite loops, this tracks the best score of any immediate
680 // children (and not deeper descendants) to avoid re-exploring the same
681 // prefix.
682 double best_child_score = 0.0;
683 int discrepancy = 0;
684 };
685 std::vector<bool> var_on_stack_;
686 std::vector<UnitMove> stack_;
687};
688
689} // namespace operations_research::sat
690
691#endif // OR_TOOLS_SAT_FEASIBILITY_JUMP_H_
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 Size() const
Returns the number of variables in the move.
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.
bool IsDone() final
Shall we delete this subsolver?
void Synchronize() final
No synchronization needed for TaskIsAvailable().
std::function< void()> GenerateTask(int64_t) final
FeasibilityJumpSolver(const absl::string_view name, SubSolver::SubsolverType type, const LinearModel *linear_model, SatParameters params, std::shared_ptr< SharedLsStates > ls_states, ModelSharedTimeLimit *shared_time_limit, SharedResponseManager *shared_response, SharedBoundsManager *shared_bounds, SharedStatistics *shared_stats, SharedStatTables *stat_tables)
void Recompute(int var)
Recompute the jump for var when GetJump(var) is next called.
std::pair< int64_t, double > GetJump(int var)
Gets the current jump delta and score, recomputing if necessary.
absl::Span< const double > Scores() const
absl::Span< const int64_t > Deltas() const
void SetComputeFunction(absl::AnyInvocable< std::pair< int64_t, double >(int) const > compute_jump)
void SetJump(int var, int64_t delta, double score)
The model "singleton" shared time limit.
Definition util.h:331
void GetChangedBounds(int id, std::vector< int > *variables, std::vector< int64_t > *new_lower_bounds, std::vector< int64_t > *new_upper_bounds)
Shared set of local search states that we work on.
void CollectStatistics(const LsState &state)
Accumulate in the relevant bucket the counters of the given states.
SharedLsStates(absl::string_view name, const SatParameters &params, SharedStatTables *stat_tables)
const SharedSolutionRepository< int64_t > & SolutionsRepository() const
Contains the table we display after the solver is done.
Definition stat_tables.h:32
Simple class to add statistics by name and print them at the end.
SubsolverType type() const
Returns the type of the subsolver.
Definition subsolver.h:99
std::string name() const
Returns the name of this SubSolver. Used in logs.
Definition subsolver.h:96
VarDomainWrapper(SharedBoundsManager *shared_bounds)
void InitializeObjective(const CpModelProto &cp_model_proto)
absl::Span< const Domain > AsSpan() 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
Block * next
const std::string name
A name for logging purposes.
int64_t value
IntVar * var
int index
STL namespace.
int64_t delta
Definition resource.cc:1709
The parameters used by the local search code.
void Randomize(const SatParameters &params, ModelRandomGenerator *random)
bool operator==(const LsOptions &o) const
friend H AbslHashValue(H h, const LsOptions &o)
In order to collect statistics by options.
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
LsCounters counters
Counters for a "non-restarted" run.
int64_t last_solution_rank
Used by LS to know the rank of the starting solution for this state.
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< bool > in_compound_weight_changed