Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
cp_model_lns.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_CP_MODEL_LNS_H_
15#define OR_TOOLS_SAT_CP_MODEL_LNS_H_
16
17#include <cmath>
18#include <cstdint>
19#include <functional>
20#include <string>
21#include <tuple>
22#include <utility>
23#include <vector>
24
25#include "absl/base/thread_annotations.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/log/check.h"
28#include "absl/random/bit_gen_ref.h"
29#include "absl/strings/string_view.h"
30#include "absl/synchronization/mutex.h"
31#include "absl/types/span.h"
32#include "google/protobuf/arena.h"
33#include "ortools/sat/cp_model.pb.h"
34#include "ortools/sat/integer.h"
35#include "ortools/sat/model.h"
36#include "ortools/sat/sat_parameters.pb.h"
39#include "ortools/sat/util.h"
41
42namespace operations_research {
43namespace sat {
44
45// Neighborhood returned by Neighborhood generators.
47 // True if neighborhood generator was able to generate a neighborhood.
48 bool is_generated = false;
49
50 // True if an optimal solution to the neighborhood is also an optimal solution
51 // to the original model.
52 bool is_reduced = false;
53
54 // True if this neighborhood was just obtained by fixing some variables.
55 bool is_simple = false;
56
57 // Specification of the delta between the initial model and the lns fragment.
58 // The delta will contains all variables from the initial model, potentially
59 // with updated domains.
60 // It can contains new variables and new constraints, and solution hinting.
61 CpModelProto delta;
62
63 // Neighborhood Id. Used to identify the neighborhood by a generator.
64 // Currently only used by WeightedRandomRelaxationNeighborhoodGenerator.
65 // TODO(user): Make sure that the id is unique for each generated
66 // neighborhood for each generator.
67 int64_t id = 0;
68
69 // Overwrites the name of the neighborhood generator in the logs.
70 std::string source_info = "";
71
72 // Statistic, only filled when is_simple is true.
75
76 // Only filled when is_simple is true. If we solve the fragment to optimality,
77 // then we can just fix the variable listed here to that optimal solution.
78 //
79 // This can happen if the neighborhood fully cover some part that are
80 // completely independent from the rest of the model. Like for instance an
81 // unused but not yet fixed variable.
82 //
83 // WARNING: all such variables should be fixed at once in a lock-like manner,
84 // because they can be multiple optimal solutions on these variables.
86};
87
88// Contains pre-computed information about a given CpModelProto that is meant
89// to be used to generate LNS neighborhood. This class can be shared between
90// more than one generator in order to reduce memory usage.
91//
92// Note that its implement the SubSolver interface to be able to Synchronize()
93// the bounds of the base problem with the external world.
95 public:
96 NeighborhoodGeneratorHelper(CpModelProto const* model_proto,
97 SatParameters const* parameters,
99 SharedBoundsManager* shared_bounds = nullptr);
100
101 // SubSolver interface.
102 bool TaskIsAvailable() override { return false; }
103 std::function<void()> GenerateTask(int64_t /*task_id*/) override {
104 return {};
105 }
106 void Synchronize() override;
107
108 // Returns the LNS fragment where the given variables are fixed to the value
109 // they take in the given solution.
111 const CpSolverResponse& base_solution,
112 const absl::flat_hash_set<int>& variables_to_fix) const;
113
114 // Returns the LNS fragment which will relax all inactive variables and all
115 // variables in relaxed_variables.
117 const CpSolverResponse& initial_solution,
118 const std::vector<int>& relaxed_variables) const;
119
120 // Returns a trivial model by fixing all active variables to the initial
121 // solution values.
122 Neighborhood FixAllVariables(const CpSolverResponse& initial_solution) const;
123
124 // Returns a neighborhood that correspond to the full problem.
126
127 // Returns a neighborhood that will just be skipped.
128 // It usually indicate that the generator failed to generated a neighborhood.
130
131 // Adds solution hinting to the neighborhood from the value of the initial
132 // solution.
133 void AddSolutionHinting(const CpSolverResponse& initial_solution,
134 CpModelProto* model_proto) const;
135
136 // Indicates if the variable can be frozen. It happens if the variable is non
137 // constant, and if it is a decision variable, or if
138 // focus_on_decision_variables is false.
139 bool IsActive(int var) const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_);
140
141 // Returns the list of "active" variables.
142 std::vector<int> ActiveVariables() const {
143 std::vector<int> result;
144 absl::ReaderMutexLock lock(&graph_mutex_);
145 result = active_variables_;
146 return result;
147 }
148
149 int NumActiveVariables() const {
150 absl::ReaderMutexLock lock(&graph_mutex_);
151 return active_variables_.size();
152 }
153
154 std::vector<int> ActiveObjectiveVariables() const {
155 std::vector<int> result;
156 absl::ReaderMutexLock lock(&graph_mutex_);
157 result = active_objective_variables_;
158 return result;
159 }
160
161 bool DifficultyMeansFullNeighborhood(double difficulty) const {
162 absl::ReaderMutexLock lock(&graph_mutex_);
163 const int target_size =
164 static_cast<int>(std::ceil(difficulty * active_variables_.size()));
165 return target_size == active_variables_.size();
166 }
167
168 // Returns the vector of active variables. The graph_mutex_ must be
169 // locked before calling this method.
170 const std::vector<int>& ActiveVariablesWhileHoldingLock() const
171 ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_) {
172 return active_variables_;
173 }
174
175 // Returns the vector of active objective variables. The graph_mutex_ must be
176 // locked before calling this method.
178 ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_) {
179 std::vector<int> result;
180 result = active_objective_variables_;
181 return result;
182 }
183
184 // Constraints <-> Variables graph.
185 // Important:
186 // - The constraint index is NOT related to the one in the cp_model.
187 // - Only non-constant var are listed in ConstraintToVar().
189 ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_) {
190 return constraint_to_var_;
191 }
193 ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_) {
194 return var_to_constraint_;
195 }
196
197 // Returns all the constraints indices of a given type.
198 absl::Span<const int> TypeToConstraints(
199 ConstraintProto::ConstraintCase type) const {
200 if (type >= type_to_constraints_.size()) return {};
201 return absl::MakeSpan(type_to_constraints_[type]);
202 }
203
204 // Filters a vector of intervals against the initial_solution, and returns
205 // only the active intervals.
206 std::vector<int> KeepActiveIntervals(
207 absl::Span<const int> unfiltered_intervals,
208 const CpSolverResponse& initial_solution) const;
209
210 // Returns the list of indices of active interval constraints according
211 // to the initial_solution and the parameter lns_focus_on_performed_intervals.
212 // If true, this method returns the list of performed intervals in the
213 // solution. If false, it returns all intervals of the model.
214 std::vector<int> GetActiveIntervals(
215 const CpSolverResponse& initial_solution) const;
216
217 // Returns the list of active rectangles appearing in no_overlap_2d
218 // constraints according to the initial_solution and the parameter
219 // lns_focus_on_performed_intervals. If true, this method returns the list of
220 // performed rectangles in the solution. If false, it returns all rectangles
221 // of the model.
222 std::vector<std::pair<int, int>> GetActiveRectangles(
223 const CpSolverResponse& initial_solution) const;
224
225 // Returns the set of unique intervals list appearing in a no_overlap,
226 // cumulative, or as a dimension of a no_overlap_2d constraint.
227 std::vector<std::vector<int>> GetUniqueIntervalSets() const;
228
229 // Returns one sub-vector per circuit or per single vehicle circuit in a
230 // routes constraints. Each circuit is non empty, and does not contain any
231 // self-looping arcs. Path are sorted, starting from the arc with the lowest
232 // tail index, and going in sequence up to the last arc before the circuit is
233 // closed. Each entry correspond to the arc literal on the circuit.
234 std::vector<std::vector<int>> GetRoutingPaths(
235 const CpSolverResponse& initial_solution) const;
236
237 // Returns all precedences extracted from the scheduling constraint and the
238 // initial solution. The precedences will be sorted by the natural order
239 // the pairs of integers.
240 std::vector<std::pair<int, int>> GetSchedulingPrecedences(
241 const absl::flat_hash_set<int>& ignored_intervals,
242 const CpSolverResponse& initial_solution, absl::BitGenRef random) const;
243
244 // Returns a copy of the problem proto with updated domains.
245 CpModelProto UpdatedModelProtoCopy() const;
246
247 // The initial problem.
248 // Note that the domain of the variables are not updated here.
249 const CpModelProto& ModelProto() const { return model_proto_; }
250 const SatParameters& Parameters() const { return parameters_; }
251
253 return *shared_response_;
254 }
255
256 // TODO(user): Refactor the class to be thread-safe instead, it should be
257 // safer and more easily maintainable. Some complication with accessing the
258 // variable<->constraint graph efficiently though.
259
260 // Note: This mutex needs to be public for thread annotations.
261 mutable absl::Mutex graph_mutex_;
262
263 // TODO(user): Display LNS statistics through the StatisticsString()
264 // method.
265
266 private:
267 // Precompute stuff that will never change. During the execution, only the
268 // domain of the variable will change, so data that only depends on the
269 // constraints need to be computed just once.
270 void InitializeHelperData();
271
272 // Recompute most of the class member. This needs to be called when the
273 // domains of the variables are updated.
274 void RecomputeHelperData();
275
276 // Indicates if a variable is fixed in the model.
277 bool IsConstant(int var) const ABSL_SHARED_LOCKS_REQUIRED(domain_mutex_);
278
279 // Returns true if the domain on the objective is constraining and we might
280 // get a lower objective value at optimum without it.
281 bool ObjectiveDomainIsConstraining() const
282 ABSL_SHARED_LOCKS_REQUIRED(domain_mutex_);
283
284 // Checks if an interval is active w.r.t. the initial_solution.
285 // An interval is inactive if and only if it is either unperformed in the
286 // solution or constant in the model.
287 bool IntervalIsActive(int index,
288 const CpSolverResponse& initial_solution) const
289 ABSL_SHARED_LOCKS_REQUIRED(domain_mutex_);
290
291 const SatParameters& parameters_;
292 const CpModelProto& model_proto_;
293 int shared_bounds_id_;
294 SharedBoundsManager* shared_bounds_;
295 SharedResponseManager* shared_response_;
296
297 // Arena holding the memory of the CpModelProto* of this class. This saves the
298 // destruction cost that can take time on problem with millions of
299 // variables/constraints.
300 google::protobuf::Arena local_arena_;
301
302 // This proto will only contain the field variables() with an updated version
303 // of the domains compared to model_proto_.variables(). We do it like this to
304 // reduce the memory footprint of the helper when the model is large.
305 //
306 // TODO(user): Use custom domain repository rather than a proto?
307 CpModelProto* model_proto_with_only_variables_ ABSL_GUARDED_BY(domain_mutex_);
308
309 // Constraints by types. This never changes.
310 std::vector<std::vector<int>> type_to_constraints_;
311
312 // Whether a model_proto_ variable appear in the objective. This never
313 // changes.
314 std::vector<bool> is_in_objective_;
315
316 // A copy of CpModelProto where we did some basic presolving to remove all
317 // constraint that are always true. The Variable-Constraint graph is based on
318 // this model. Note that only the constraints field is present here.
319 CpModelProto* simplified_model_proto_ ABSL_GUARDED_BY(graph_mutex_);
320
321 // Variable-Constraint graph.
322 // We replace an interval by its variables in the scheduling constraints.
323 //
324 // TODO(user): Note that the objective is not considered here. Which is fine
325 // except if the objective domain is constraining.
326 CompactVectorVector<int, int> constraint_to_var_
327 ABSL_GUARDED_BY(graph_mutex_);
328 CompactVectorVector<int, int> var_to_constraint_
329 ABSL_GUARDED_BY(graph_mutex_);
330
331 // Connected components of the variable-constraint graph. If a variable is
332 // constant, it will not appear in any component and
333 // var_to_component_index_[var] will be -1.
334 std::vector<std::vector<int>> components_ ABSL_GUARDED_BY(graph_mutex_);
335 std::vector<int> var_to_component_index_ ABSL_GUARDED_BY(graph_mutex_);
336
337 // The set of active variables which is currently the list of non-constant
338 // variables. It is stored both as a list and as a set (using a Boolean
339 // vector).
340 std::vector<bool> active_variables_set_ ABSL_GUARDED_BY(graph_mutex_);
341 std::vector<int> active_variables_ ABSL_GUARDED_BY(graph_mutex_);
342
343 // The list of non constant variables appearing in the objective.
344 std::vector<int> active_objective_variables_ ABSL_GUARDED_BY(graph_mutex_);
345
346 std::vector<int> tmp_row_;
347
348 mutable absl::Mutex domain_mutex_;
349};
350
351// Base class for a CpModelProto neighborhood generator.
353 public:
354 NeighborhoodGenerator(absl::string_view name,
355 NeighborhoodGeneratorHelper const* helper)
356 : name_(name), helper_(*helper), difficulty_(0.5) {}
357 virtual ~NeighborhoodGenerator() = default;
358
359 // Generates a "local" subproblem for the given seed.
360 //
361 // The difficulty will be in [0, 1] and is related to the asked neighborhood
362 // size (and thus local problem difficulty). A difficulty of 0.0 means empty
363 // neighborhood and a difficulty of 1.0 means the full problem. The algorithm
364 // should try to generate a neighborhood according to this difficulty which
365 // will be dynamically adjusted depending on whether or not we can solve the
366 // subproblem in a given time limit.
367 //
368 // The given initial_solution should contain a feasible solution to the
369 // initial CpModelProto given to this class. Any solution to the returned
370 // CPModelProto should also be valid solution to the same initial model.
371 //
372 // This function should be thread-safe.
373 virtual Neighborhood Generate(const CpSolverResponse& initial_solution,
374 double difficulty, absl::BitGenRef random) = 0;
375
376 // Returns true if the neighborhood generator can generate a neighborhood.
377 virtual bool ReadyToGenerate() const;
378
379 // Uses UCB1 algorithm to compute the score (Multi armed bandit problem).
380 // Details are at
381 // https://lilianweng.github.io/lil-log/2018/01/23/the-multi-armed-bandit-problem-and-its-solutions.html.
382 // 'total_num_calls' should be the sum of calls across all generators part of
383 // the multi armed bandit problem.
384 // If the generator is called less than 10 times then the method returns
385 // infinity as score in order to get more data about the generator
386 // performance.
387 double GetUCBScore(int64_t total_num_calls) const;
388
389 // Adds solve data about one "solved" neighborhood.
390 struct SolveData {
391 // The status of the sub-solve.
392 CpSolverStatus status = CpSolverStatus::UNKNOWN;
393
394 // The difficulty when this neighborhood was generated.
395 double difficulty = 0.0;
396
397 // The deterministic time limit given to the solver for this neighborhood.
398 double deterministic_limit = 0.0;
399
400 // The time it took to solve this neighborhood.
401 double deterministic_time = 0.0;
402
403 // Objective information. These only refer to the "internal" objective
404 // without scaling or offset so we are exact and it is always in the
405 // minimization direction.
406 // - The initial best objective is the one of the best known solution at the
407 // time the neighborhood was generated.
408 // - The base objective is the one of the base solution from which this
409 // neighborhood was generated.
410 // - The new objective is the objective of the best solution found by
411 // solving the neighborhood.
412 IntegerValue initial_best_objective = IntegerValue(0);
413 IntegerValue base_objective = IntegerValue(0);
414 IntegerValue new_objective = IntegerValue(0);
415
416 // This is just used to construct a deterministic order for the updates.
417 bool operator<(const SolveData& o) const {
418 return std::tie(status, difficulty, deterministic_limit,
419 deterministic_time, initial_best_objective,
420 base_objective, new_objective) <
421 std::tie(o.status, o.difficulty, o.deterministic_limit,
424 }
425 };
427 absl::MutexLock mutex_lock(&generator_mutex_);
428 solve_data_.push_back(data);
429 }
430
431 // Process all the recently added solve data and update this generator
432 // score and difficulty. This returns the sum of the deterministic time of
433 // each SolveData.
434 double Synchronize();
435
436 // Returns a short description of the generator.
437 std::string name() const { return name_; }
438
439 // Number of times this generator was called.
440 int64_t num_calls() const {
441 absl::MutexLock mutex_lock(&generator_mutex_);
442 return num_calls_;
443 }
444
445 // Number of time the neighborhood was fully solved (OPTIMAL/INFEASIBLE).
446 int64_t num_fully_solved_calls() const {
447 absl::MutexLock mutex_lock(&generator_mutex_);
448 return num_fully_solved_calls_;
449 }
450
451 // Out of num_calls(), how many improved the given solution.
452 int64_t num_improving_calls() const {
453 absl::MutexLock mutex_lock(&generator_mutex_);
454 return num_improving_calls_;
455 }
456
457 // Returns the number of the last calls to this generator that didn't improve
458 // the best solution. Note that this count improvement to the best known
459 // solution not the base one used to generate one neighborhood.
461 absl::MutexLock mutex_lock(&generator_mutex_);
462 return num_consecutive_non_improving_calls_;
463 }
464
465 // The current difficulty of this generator
466 double difficulty() const {
467 absl::MutexLock mutex_lock(&generator_mutex_);
468 return difficulty_.value();
469 }
470
471 // The current time limit that the sub-solve should use on this generator.
472 double deterministic_limit() const {
473 absl::MutexLock mutex_lock(&generator_mutex_);
474 return deterministic_limit_;
475 }
476
477 protected:
478 const std::string name_;
480 mutable absl::Mutex generator_mutex_;
481
482 private:
483 std::vector<SolveData> solve_data_;
484
485 // Current parameters to be used when generating/solving a neighborhood with
486 // this generator. Only updated on Synchronize().
487 AdaptiveParameterValue difficulty_;
488 double deterministic_limit_ = 0.1;
489
490 // Current statistics of the last solved neighborhood.
491 // Only updated on Synchronize().
492 int64_t num_calls_ = 0;
493 int64_t num_fully_solved_calls_ = 0;
494 int64_t num_improving_calls_ = 0;
495 int64_t num_consecutive_non_improving_calls_ = 0;
496 int64_t next_time_limit_bump_ = 50;
497 double current_average_ = 0.0;
498};
499
500// Pick a random subset of variables.
501//
502// TODO(user): In the presence of connected components, this should just work
503// on one of them.
505 public:
507 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
508 : NeighborhoodGenerator(name, helper) {}
509 Neighborhood Generate(const CpSolverResponse& initial_solution,
510 double difficulty, absl::BitGenRef random) final;
511};
512
513// Pick a random subset of constraints and relax all the variables of these
514// constraints. Note that to satisfy the difficulty, we might not relax all the
515// variable of the "last" constraint.
516//
517// TODO(user): In the presence of connected components, this should just work
518// on one of them.
520 public:
522 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
523 : NeighborhoodGenerator(name, helper) {}
524 Neighborhood Generate(const CpSolverResponse& initial_solution,
525 double difficulty, absl::BitGenRef random) final;
526};
527
528// Pick a random subset of variables that are constructed by a BFS in the
529// variable <-> constraint graph. That is, pick a random variable, then all the
530// variable connected by some constraint to the first one, and so on. The
531// variable of the last "level" are selected randomly.
532//
533// Note that in the presence of connected component, this works correctly
534// already.
536 public:
538 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
539 : NeighborhoodGenerator(name, helper) {}
540 Neighborhood Generate(const CpSolverResponse& initial_solution,
541 double difficulty, absl::BitGenRef random) final;
542};
543
544// This randomly extend a working set of variable by one variable directly
545// connected to that set.
547 public:
549 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
550 : NeighborhoodGenerator(name, helper) {}
551 Neighborhood Generate(const CpSolverResponse& initial_solution,
552 double difficulty, absl::BitGenRef random) final;
553};
554
555// Pick a random subset of constraint and relax all of their variables. We are a
556// bit smarter than this because after the first constraint is selected, we only
557// select constraints that share at least one variable with the already selected
558// constraints. The variable from the "last" constraint are selected randomly.
560 public:
562 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
563 : NeighborhoodGenerator(name, helper) {}
564 Neighborhood Generate(const CpSolverResponse& initial_solution,
565 double difficulty, absl::BitGenRef random) final;
566};
567
568// The idea here is to try to generate a random neighborhood incrementally in
569// such a way that we have at various point a "minimum connection" in term of
570// constraints or variable to the outside world.
571//
572// This is inspired by what would be a good neighborhood if one where to use
573// a tree decomposition of the constraint-variable graph with small treewidth.
574//
575// TODO(user): Doing the full heuristic treewidth decomposition is probably
576// better because when we grow the current neighborhood, just using local
577// connection to the current candidate is probably not enough to orient the
578// search towards a good final neighborhood.
580 public:
582 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
583 : NeighborhoodGenerator(name, helper) {}
584 Neighborhood Generate(const CpSolverResponse& initial_solution,
585 double difficulty, absl::BitGenRef random) final;
586};
587
588// Solves a local branching LP and greedily picks a set of variables with the
589// largest differences between the initial and local branching LP solutions,
590// breaking ties uniformly at random.
591//
592// This is based on Huang et al., "Local Branching Relaxation Heuristics for
593// Integer Linear Programs", 2023.
595 : public NeighborhoodGenerator {
596 public:
597 // TODO(user): Restructure code so that we avoid circular dependency with
598 // solving functions. For now, we use solve_callback.
600 NeighborhoodGeneratorHelper const* helper, absl::string_view name,
601 std::function<void(CpModelProto, Model*)> solve_callback,
602 ModelSharedTimeLimit* const global_time_limit)
603 : NeighborhoodGenerator(name, helper),
604 solve_callback_(std::move(solve_callback)),
605 global_time_limit_(global_time_limit) {}
606 Neighborhood Generate(const CpSolverResponse& initial_solution,
607 double difficulty, absl::BitGenRef random) final;
608
609 private:
610 const std::function<void(CpModelProto, Model*)> solve_callback_;
611 ModelSharedTimeLimit* const global_time_limit_;
612};
613
614// Helper method for the scheduling neighborhood generators. Returns a
615// neighborhood defined from the given set of intervals to relax. For each
616// scheduling constraint, it adds strict relation order between the non-relaxed
617// intervals.
619 absl::Span<const int> intervals_to_relax,
620 absl::Span<const int> variables_to_fix,
621 const CpSolverResponse& initial_solution, absl::BitGenRef random,
622 const NeighborhoodGeneratorHelper& helper);
623
624// Helper method for the scheduling neighborhood generators. Returns a
625// full neighborhood enriched with the set or precedences passed to the generate
626// method.
628 absl::Span<const std::pair<int, int>> precedences,
629 const CpSolverResponse& initial_solution,
630 const NeighborhoodGeneratorHelper& helper);
631
632// Only make sense for scheduling problem. This select a random set of interval
633// of the problem according to the difficulty. Then, for each scheduling
634// constraints, it adds strict relation order between the non-relaxed intervals.
636 : public NeighborhoodGenerator {
637 public:
639 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
640 : NeighborhoodGenerator(name, helper) {}
641
642 Neighborhood Generate(const CpSolverResponse& initial_solution,
643 double difficulty, absl::BitGenRef random) final;
644};
645
646// Only make sense for scheduling problem. This select a random set of
647// precedences between intervals of the problem according to the difficulty.
648// These precedences are extracted from the scheduling constraints and their
649// configuration in the current solution. Then it adds the kept precedences to
650// the model.
652 : public NeighborhoodGenerator {
653 public:
655 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
656 : NeighborhoodGenerator(name, helper) {}
657
658 Neighborhood Generate(const CpSolverResponse& initial_solution,
659 double difficulty, absl::BitGenRef random) final;
660};
661
662// Similar to SchedulingNeighborhoodGenerator except the set of intervals that
663// are relaxed are from a specific random time interval.
665 public:
667 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
668 : NeighborhoodGenerator(name, helper) {}
669
670 Neighborhood Generate(const CpSolverResponse& initial_solution,
671 double difficulty, absl::BitGenRef random) final;
672};
673
674// Similar to SchedulingTimeWindowNeighborhoodGenerator except that it relaxes
675// one independent time window per resource (1 for each dimension in the
676// no_overlap_2d case).
678 : public NeighborhoodGenerator {
679 public:
681 NeighborhoodGeneratorHelper const* helper,
682 const std::vector<std::vector<int>>& intervals_in_constraints,
683 absl::string_view name)
684 : NeighborhoodGenerator(name, helper),
685 intervals_in_constraints_(intervals_in_constraints) {}
686
687 Neighborhood Generate(const CpSolverResponse& initial_solution,
688 double difficulty, absl::BitGenRef random) final;
689
690 private:
691 const std::vector<std::vector<int>> intervals_in_constraints_;
692};
693
694// Only make sense for problems with no_overlap_2d constraints. This select a
695// random set of rectangles (i.e. a pair of intervals) of the problem according
696// to the difficulty. Then fix all variables in the selected intervals.
698 : public NeighborhoodGenerator {
699 public:
701 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
702 : NeighborhoodGenerator(name, helper) {}
703
704 Neighborhood Generate(const CpSolverResponse& initial_solution,
705 double difficulty, absl::BitGenRef random) final;
706};
707
708// Only make sense for problems with no_overlap_2d constraints. This select a
709// random set of rectangles (i.e. a pair of intervals) of the problem according
710// to the difficulty. Then add all implied precedences from the current
711// positions of the rectangles in this selected subset.
713 : public NeighborhoodGenerator {
714 public:
716 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
717 : NeighborhoodGenerator(name, helper) {}
718
719 Neighborhood Generate(const CpSolverResponse& initial_solution,
720 double difficulty, absl::BitGenRef random) final;
721};
722
723// Only make sense for problems with no_overlap_2d constraints. This select a
724// slice on one dimension, and fix the variables of all rectangles not strictly
725// included in this slice.
727 public:
729 NeighborhoodGeneratorHelper const* helper, absl::string_view name)
730 : NeighborhoodGenerator(name, helper) {}
731
732 Neighborhood Generate(const CpSolverResponse& initial_solution,
733 double difficulty, absl::BitGenRef random) final;
734};
735
736// This routing based LNS generator will relax random arcs in all the paths of
737// the circuit or routes constraints.
739 public:
743
744 Neighborhood Generate(const CpSolverResponse& initial_solution,
745 double difficulty, absl::BitGenRef random) final;
746};
747
748// This routing based LNS generator will relax small sequences of arcs randomly
749// chosen in all the paths of the circuit or routes constraints.
751 public:
753 absl::string_view name)
754 : NeighborhoodGenerator(name, helper) {}
755
756 Neighborhood Generate(const CpSolverResponse& initial_solution,
757 double difficulty, absl::BitGenRef random) final;
758};
759
760// This routing based LNS generator aims are relaxing one full path, and make
761// some room on the other paths to absorb the nodes of the relaxed path.
762//
763// In order to do so, it will relax the first and the last arc of each path in
764// the circuit or routes constraints. Then it will relax all arc literals in one
765// random path. Then it will relax random arcs in the remaining paths until it
766// reaches the given difficulty.
768 public:
772
773 Neighborhood Generate(const CpSolverResponse& initial_solution,
774 double difficulty, absl::BitGenRef random) final;
775};
776
777// Generates a neighborhood by fixing the variables to solutions reported in
778// various repositories. This is inspired from RINS published in "Exploring
779// relaxation induced neighborhoods to improve MIP solutions" 2004 by E. Danna
780// et.
781//
782// If incomplete_solutions is provided, this generates a neighborhood by fixing
783// the variable values to a solution in the SharedIncompleteSolutionManager and
784// ignores the other repositories.
785//
786// Otherwise, if response_manager is not provided, this generates a neighborhood
787// using only the linear/general relaxation values. The domain of the variables
788// are reduced to the integer values around their lp solution/relaxation
789// solution values. This was published in "RENS – The Relaxation Enforced
790// Neighborhood" 2009 by Timo Berthold.
792 public:
794 NeighborhoodGeneratorHelper const* helper,
795 const SharedResponseManager* response_manager,
796 const SharedLPSolutionRepository* lp_solutions,
797 SharedIncompleteSolutionManager* incomplete_solutions,
798 absl::string_view name)
799 : NeighborhoodGenerator(name, helper),
800 response_manager_(response_manager),
801 lp_solutions_(lp_solutions),
802 incomplete_solutions_(incomplete_solutions) {
803 CHECK(lp_solutions_ != nullptr);
804 CHECK(incomplete_solutions != nullptr);
805 }
806
807 // Both initial solution and difficulty values are ignored.
808 Neighborhood Generate(const CpSolverResponse& initial_solution,
809 double difficulty, absl::BitGenRef random) final;
810
811 // Returns true if the required solutions are available.
812 bool ReadyToGenerate() const override;
813
814 private:
815 const SharedResponseManager* response_manager_;
816 const SharedLPSolutionRepository* lp_solutions_;
817 SharedIncompleteSolutionManager* incomplete_solutions_;
818};
819
820} // namespace sat
821} // namespace operations_research
822
823#endif // OR_TOOLS_SAT_CP_MODEL_LNS_H_
ArcGraphNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
ConstraintGraphNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
DecompositionGraphNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
LocalBranchingLpBasedNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name, std::function< void(CpModelProto, Model *)> solve_callback, ModelSharedTimeLimit *const global_time_limit)
The model "singleton" shared time limit.
Definition util.h:331
Neighborhood FullNeighborhood() const
Returns a neighborhood that correspond to the full problem.
bool IsActive(int var) const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
CpModelProto UpdatedModelProtoCopy() const
Returns a copy of the problem proto with updated domains.
Neighborhood RelaxGivenVariables(const CpSolverResponse &initial_solution, const std::vector< int > &relaxed_variables) const
Neighborhood FixAllVariables(const CpSolverResponse &initial_solution) const
Neighborhood FixGivenVariables(const CpSolverResponse &base_solution, const absl::flat_hash_set< int > &variables_to_fix) const
const std::vector< int > & ActiveVariablesWhileHoldingLock() const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
const CompactVectorVector< int, int > & VarToConstraint() const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
std::vector< std::pair< int, int > > GetActiveRectangles(const CpSolverResponse &initial_solution) const
const SharedResponseManager & shared_response() const
NeighborhoodGeneratorHelper(CpModelProto const *model_proto, SatParameters const *parameters, SharedResponseManager *shared_response, SharedBoundsManager *shared_bounds=nullptr)
std::vector< int > ActiveVariables() const
Returns the list of "active" variables.
std::vector< std::vector< int > > GetUniqueIntervalSets() const
const CompactVectorVector< int, int > & ConstraintToVar() const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
bool DifficultyMeansFullNeighborhood(double difficulty) const
void AddSolutionHinting(const CpSolverResponse &initial_solution, CpModelProto *model_proto) const
std::vector< std::vector< int > > GetRoutingPaths(const CpSolverResponse &initial_solution) const
std::vector< int > ActiveObjectiveVariablesWhileHoldingLock() const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_)
std::function< void()> GenerateTask(int64_t) override
std::vector< int > GetActiveIntervals(const CpSolverResponse &initial_solution) const
std::vector< std::pair< int, int > > GetSchedulingPrecedences(const absl::flat_hash_set< int > &ignored_intervals, const CpSolverResponse &initial_solution, absl::BitGenRef random) const
absl::Span< const int > TypeToConstraints(ConstraintProto::ConstraintCase type) const
Returns all the constraints indices of a given type.
bool TaskIsAvailable() override
SubSolver interface.
std::vector< int > KeepActiveIntervals(absl::Span< const int > unfiltered_intervals, const CpSolverResponse &initial_solution) const
Base class for a CpModelProto neighborhood generator.
virtual Neighborhood Generate(const CpSolverResponse &initial_solution, double difficulty, absl::BitGenRef random)=0
double deterministic_limit() const
The current time limit that the sub-solve should use on this generator.
int64_t num_improving_calls() const
Out of num_calls(), how many improved the given solution.
std::string name() const
Returns a short description of the generator.
double difficulty() const
The current difficulty of this generator.
int64_t num_calls() const
Number of times this generator was called.
const NeighborhoodGeneratorHelper & helper_
int64_t num_fully_solved_calls() const
Number of time the neighborhood was fully solved (OPTIMAL/INFEASIBLE).
NeighborhoodGenerator(absl::string_view name, NeighborhoodGeneratorHelper const *helper)
RandomIntervalSchedulingNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
RandomPrecedenceSchedulingNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
RandomPrecedencesPackingNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
RandomRectanglesPackingNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
RelaxRandomConstraintsGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
RelaxRandomVariablesGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
RelaxationInducedNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, const SharedResponseManager *response_manager, const SharedLPSolutionRepository *lp_solutions, SharedIncompleteSolutionManager *incomplete_solutions, absl::string_view name)
RoutingFullPathNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
RoutingPathNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
RoutingRandomNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
SchedulingResourceWindowsNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, const std::vector< std::vector< int > > &intervals_in_constraints, absl::string_view name)
SchedulingTimeWindowNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
SlicePackingNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
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
VariableGraphNeighborhoodGenerator(NeighborhoodGeneratorHelper const *helper, absl::string_view name)
SatParameters parameters
const std::string name
A name for logging purposes.
IntVar * var
absl::Status status
Definition g_gurobi.cc:44
int index
Neighborhood GenerateSchedulingNeighborhoodFromIntervalPrecedences(const absl::Span< const std::pair< int, int > > precedences, const CpSolverResponse &initial_solution, const NeighborhoodGeneratorHelper &helper)
Neighborhood GenerateSchedulingNeighborhoodFromRelaxedIntervals(absl::Span< const int > intervals_to_relax, absl::Span< const int > variables_to_fix, const CpSolverResponse &initial_solution, absl::BitGenRef random, const NeighborhoodGeneratorHelper &helper)
In SWIG mode, we don't want anything besides these top-level includes.
STL namespace.
Adds solve data about one "solved" neighborhood.
double deterministic_time
The time it took to solve this neighborhood.
double difficulty
The difficulty when this neighborhood was generated.
double deterministic_limit
The deterministic time limit given to the solver for this neighborhood.
CpSolverStatus status
The status of the sub-solve.
bool operator<(const SolveData &o) const
This is just used to construct a deterministic order for the updates.
Neighborhood returned by Neighborhood generators.
std::string source_info
Overwrites the name of the neighborhood generator in the logs.
bool is_simple
True if this neighborhood was just obtained by fixing some variables.
int num_relaxed_variables
Statistic, only filled when is_simple is true.
bool is_generated
True if neighborhood generator was able to generate a neighborhood.
std::vector< int > variables_that_can_be_fixed_to_local_optimum