1 // Copyright 2010-2021 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 <cstdint>
18 #include <vector>
19 
20 #include "absl/container/flat_hash_map.h"
21 #include "absl/random/bit_gen_ref.h"
22 #include "absl/synchronization/mutex.h"
23 #include "absl/types/span.h"
24 #include "ortools/base/integral_types.h"
25 #include "ortools/sat/cp_model.pb.h"
26 #include "ortools/sat/cp_model_presolve.h"
27 #include "ortools/sat/model.h"
28 #include "ortools/sat/subsolver.h"
29 #include "ortools/sat/synchronization.h"
30 #include "ortools/util/adaptative_parameter_value.h"
31 #include "ortools/util/logging.h"
32 
33 namespace operations_research {
34 namespace sat {
35 
36 // Neighborhood returned by Neighborhood generators.
37 struct Neighborhood {
38   // True if neighborhood generator was able to generate a neighborhood.
39   bool is_generated = false;
40 
41   // True if an optimal solution to the neighborhood is also an optimal solution
42   // to the original model.
43   bool is_reduced = false;
44 
45   // True if this neighborhood was just obtained by fixing some variables.
46   bool is_simple = false;
47 
48   // Specification of the delta between the initial model and the lns fragment.
49   // The delta will contains all variables from the initial model, potentially
50   // with updated domains.
51   // It can contains new variables and new constraints, and solution hinting.
52   CpModelProto delta;
53   std::vector<int> constraints_to_ignore;
54 
55   // Neighborhood Id. Used to identify the neighborhood by a generator.
56   // Currently only used by WeightedRandomRelaxationNeighborhoodGenerator.
57   // TODO(user): Make sure that the id is unique for each generated
58   // neighborhood for each generator.
59   int64_t id = 0;
60 
61   // Used for identifying the source of the neighborhood if it is generated
62   // using solution repositories.
63   std::string source_info = "";
64 
65   // Statistic, only filled when is_simple is true.
66   int num_relaxed_variables = 0;
67   int num_relaxed_variables_in_objective = 0;
68 
69   // Only filled when is_simple is true. If we solve the fragment to optimality,
70   // then we can just fix the variable listed here to that optimal solution.
71   //
72   // This can happen if the neighborhood fully cover some part that are
73   // completely independent from the rest of the model. Like for instance an
74   // unused but not yet fixed variable.
75   //
76   // WARNING: all such variables should be fixed at once in a lock-like manner,
77   // because they can be multiple optimal solutions on these variables.
78   std::vector<int> variables_that_can_be_fixed_to_local_optimum;
79 };
80 
81 // Contains pre-computed information about a given CpModelProto that is meant
82 // to be used to generate LNS neighborhood. This class can be shared between
83 // more than one generator in order to reduce memory usage.
84 //
85 // Note that its implement the SubSolver interface to be able to Synchronize()
86 // the bounds of the base problem with the external world.
87 class NeighborhoodGeneratorHelper : public SubSolver {
88  public:
89   NeighborhoodGeneratorHelper(CpModelProto const* model_proto,
90                               SatParameters const* parameters,
91                               SharedResponseManager* shared_response,
92                               SharedTimeLimit* shared_time_limit = nullptr,
93                               SharedBoundsManager* shared_bounds = nullptr);
94 
95   // SubSolver interface.
TaskIsAvailable()96   bool TaskIsAvailable() override { return false; }
GenerateTask(int64_t task_id)97   std::function<void()> GenerateTask(int64_t task_id) override { return {}; }
98   void Synchronize() override;
99 
100   // Returns the LNS fragment where the given variables are fixed to the value
101   // they take in the given solution.
102   Neighborhood FixGivenVariables(
103       const CpSolverResponse& base_solution,
104       const absl::flat_hash_set<int>& variables_to_fix) const;
105 
106   // Returns the neighborhood where the given constraints are removed.
107   Neighborhood RemoveMarkedConstraints(
108       const std::vector<int>& constraints_to_remove) const;
109 
110   // Returns the LNS fragment which will relax all inactive variables and all
111   // variables in relaxed_variables.
112   Neighborhood RelaxGivenVariables(
113       const CpSolverResponse& initial_solution,
114       const std::vector<int>& relaxed_variables) const;
115 
116   // Returns a trivial model by fixing all active variables to the initial
117   // solution values.
118   Neighborhood FixAllVariables(const CpSolverResponse& initial_solution) const;
119 
120   // Returns a neighborhood that correspond to the full problem.
121   Neighborhood FullNeighborhood() const;
122 
123   // Returns a neighborhood that will just be skipped.
124   // It usually indicate that the generator failed to generated a neighborhood.
125   Neighborhood NoNeighborhood() const;
126 
127   // Adds solution hinting to the neighborhood from the value of the initial
128   // solution.
129   void AddSolutionHinting(const CpSolverResponse& initial_solution,
130                           CpModelProto* model_proto) const;
131 
132   // Indicates if the variable can be frozen. It happens if the variable is non
133   // constant, and if it is a decision variable, or if
134   // focus_on_decision_variables is false.
135   bool IsActive(int var) const ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_);
136 
137   // Returns the list of "active" variables.
ActiveVariables()138   std::vector<int> ActiveVariables() const {
139     std::vector<int> result;
140     absl::ReaderMutexLock lock(&graph_mutex_);
141     result = active_variables_;
142     return result;
143   }
144 
NumActiveVariables()145   int NumActiveVariables() const {
146     absl::ReaderMutexLock lock(&graph_mutex_);
147     return active_variables_.size();
148   }
149 
DifficultyMeansFullNeighborhood(double difficulty)150   bool DifficultyMeansFullNeighborhood(double difficulty) const {
151     absl::ReaderMutexLock lock(&graph_mutex_);
152     const int target_size = std::ceil(difficulty * active_variables_.size());
153     return target_size == active_variables_.size();
154   }
155 
156   // Returns the vector of active variables. The graph_mutex_ must be
157   // locked before calling this method.
ActiveVariablesWhileHoldingLock()158   const std::vector<int>& ActiveVariablesWhileHoldingLock() const
159       ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_) {
160     return active_variables_;
161   }
162 
163   // Constraints <-> Variables graph.
164   // Note that only non-constant variable are listed here.
ConstraintToVar()165   const std::vector<std::vector<int>>& ConstraintToVar() const
166       ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_) {
167     return constraint_to_var_;
168   }
VarToConstraint()169   const std::vector<std::vector<int>>& VarToConstraint() const
170       ABSL_SHARED_LOCKS_REQUIRED(graph_mutex_) {
171     return var_to_constraint_;
172   }
173 
174   // Returns all the constraints indices of a given type.
TypeToConstraints(ConstraintProto::ConstraintCase type)175   const absl::Span<const int> TypeToConstraints(
176       ConstraintProto::ConstraintCase type) const {
177     if (type >= type_to_constraints_.size()) return {};
178     return absl::MakeSpan(type_to_constraints_[type]);
179   }
180 
181   // Returns the list of indices of active interval constraints according
182   // to the initial_solution and the parameter lns_focus_on_performed_intervals.
183   // If true, this method returns the list of performed intervals in the
184   // solution. If false, it returns all intervals of the model.
185   std::vector<int> GetActiveIntervals(
186       const CpSolverResponse& initial_solution) const;
187 
188   // Returns one sub-vector per circuit or per single vehicle ciruit in a routes
189   // constraints. Each circuit is non empty, and does not contain any
190   // self-looping arcs. Path are sorted, starting from the arc with the lowest
191   // tail index, and going in sequence up to the last arc before the circuit is
192   // closed. Each entry correspond to the arc literal on the circuit.
193   std::vector<std::vector<int>> GetRoutingPaths(
194       const CpSolverResponse& initial_solution) const;
195 
196   // The initial problem.
197   // Note that the domain of the variables are not updated here.
ModelProto()198   const CpModelProto& ModelProto() const { return model_proto_; }
Parameters()199   const SatParameters& Parameters() const { return parameters_; }
200 
shared_response()201   const SharedResponseManager& shared_response() const {
202     return *shared_response_;
203   }
204 
205   // TODO(user): Refactor the class to be thread-safe instead, it should be
206   // safer and more easily maintenable. Some complication with accessing the
207   // variable<->constraint graph efficiently though.
208 
209   // Note: This mutex needs to be public for thread annotations.
210   mutable absl::Mutex graph_mutex_;
211 
212   // TODO(user): Display LNS statistics through the StatisticsString()
213   // method.
214 
215  private:
216   // Precompute stuff that will never change. During the execution, only the
217   // domain of the variable will change, so data that only depends on the
218   // constraints need to be computed just once.
219   void InitializeHelperData();
220 
221   // Recompute most of the class member. This needs to be called when the
222   // domains of the variables are updated.
223   void RecomputeHelperData();
224 
225   // Indicates if a variable is fixed in the model.
226   bool IsConstant(int var) const ABSL_SHARED_LOCKS_REQUIRED(domain_mutex_);
227 
228   // Returns true if the domain on the objective is constraining and we might
229   // get a lower objective value at optimum without it.
230   bool ObjectiveDomainIsConstraining() const
231       ABSL_SHARED_LOCKS_REQUIRED(domain_mutex_);
232 
233   const SatParameters& parameters_;
234   const CpModelProto& model_proto_;
235   int shared_bounds_id_;
236   SharedTimeLimit* shared_time_limit_;
237   SharedBoundsManager* shared_bounds_;
238   SharedResponseManager* shared_response_;
239   SharedRelaxationSolutionRepository* shared_relaxation_solutions_;
240 
241   // This proto will only contain the field variables() with an updated version
242   // of the domains compared to model_proto_.variables(). We do it like this to
243   // reduce the memory footprint of the helper when the model is large.
244   //
245   // TODO(user): Use custom domain repository rather than a proto?
246   CpModelProto model_proto_with_only_variables_ ABSL_GUARDED_BY(domain_mutex_);
247 
248   // Constraints by types. This never changes.
249   std::vector<std::vector<int>> type_to_constraints_;
250 
251   // Whether a model_proto_ variable appear in the objective. This never
252   // changes.
253   std::vector<bool> is_in_objective_;
254 
255   // A copy of CpModelProto where we did some basic presolving to remove all
256   // constraint that are always true. The Variable-Constraint graph is based on
257   // this model. Note that only the constraints field is present here.
258   CpModelProto simplied_model_proto_ ABSL_GUARDED_BY(graph_mutex_);
259 
260   // Variable-Constraint graph.
261   // We replace an interval by its variables in the scheduling constraints.
262   //
263   // TODO(user): Note that the objective is not considered here. Which is fine
264   // except if the objective domain is constraining.
265   std::vector<std::vector<int>> constraint_to_var_
266       ABSL_GUARDED_BY(graph_mutex_);
267   std::vector<std::vector<int>> var_to_constraint_
268       ABSL_GUARDED_BY(graph_mutex_);
269 
270   // Connected components of the variable-constraint graph. If a variable is
271   // constant, it will not appear in any component and
272   // var_to_component_index_[var] will be -1.
273   std::vector<std::vector<int>> components_ ABSL_GUARDED_BY(graph_mutex_);
274   std::vector<int> var_to_component_index_ ABSL_GUARDED_BY(graph_mutex_);
275 
276   // The set of active variables which is currently the list of non-constant
277   // variables. It is stored both as a list and as a set (using a Boolean
278   // vector).
279   std::vector<bool> active_variables_set_ ABSL_GUARDED_BY(graph_mutex_);
280   std::vector<int> active_variables_ ABSL_GUARDED_BY(graph_mutex_);
281 
282   mutable absl::Mutex domain_mutex_;
283 };
284 
285 // Base class for a CpModelProto neighborhood generator.
286 class NeighborhoodGenerator {
287  public:
NeighborhoodGenerator(const std::string & name,NeighborhoodGeneratorHelper const * helper)288   NeighborhoodGenerator(const std::string& name,
289                         NeighborhoodGeneratorHelper const* helper)
290       : name_(name), helper_(*helper), difficulty_(0.5) {}
~NeighborhoodGenerator()291   virtual ~NeighborhoodGenerator() {}
292 
293   // Generates a "local" subproblem for the given seed.
294   //
295   // The difficulty will be in [0, 1] and is related to the asked neighborhood
296   // size (and thus local problem difficulty). A difficulty of 0.0 means empty
297   // neighborhood and a difficulty of 1.0 means the full problem. The algorithm
298   // should try to generate a neighborhood according to this difficulty which
299   // will be dynamically adjusted depending on whether or not we can solve the
300   // subproblem in a given time limit.
301   //
302   // The given initial_solution should contain a feasible solution to the
303   // initial CpModelProto given to this class. Any solution to the returned
304   // CPModelProto should also be valid solution to the same initial model.
305   //
306   // This function should be thread-safe.
307   virtual Neighborhood Generate(const CpSolverResponse& initial_solution,
308                                 double difficulty, absl::BitGenRef random) = 0;
309 
310   // Returns true if the neighborhood generator can generate a neighborhood.
311   virtual bool ReadyToGenerate() const;
312 
313   // Returns true if the neighborhood generator generates relaxation of the
314   // given problem.
IsRelaxationGenerator()315   virtual bool IsRelaxationGenerator() const { return false; }
316 
317   // Uses UCB1 algorithm to compute the score (Multi armed bandit problem).
318   // Details are at
319   // https://lilianweng.github.io/lil-log/2018/01/23/the-multi-armed-bandit-problem-and-its-solutions.html.
320   // 'total_num_calls' should be the sum of calls across all generators part of
321   // the multi armed bandit problem.
322   // If the generator is called less than 10 times then the method returns
323   // infinity as score in order to get more data about the generator
324   // performance.
325   double GetUCBScore(int64_t total_num_calls) const;
326 
327   // Adds solve data about one "solved" neighborhood.
328   struct SolveData {
329     // Neighborhood Id. Used to identify the neighborhood by a generator.
330     // Currently only used by WeightedRandomRelaxationNeighborhoodGenerator.
331     int64_t neighborhood_id = 0;
332 
333     // The status of the sub-solve.
334     CpSolverStatus status = CpSolverStatus::UNKNOWN;
335 
336     // The difficulty when this neighborhood was generated.
337     double difficulty = 0.0;
338 
339     // The determinitic time limit given to the solver for this neighborhood.
340     double deterministic_limit = 0.0;
341 
342     // The time it took to solve this neighborhood.
343     double deterministic_time = 0.0;
344 
345     // Objective information. These only refer to the "internal" objective
346     // without scaling or offset so we are exact and it is always in the
347     // minimization direction.
348     // - The initial best objective is the one of the best known solution at the
349     //   time the neighborhood was generated.
350     // - The base objective is the one of the base solution from which this
351     //   neighborhood was generated.
352     // - The new objective is the objective of the best solution found by
353     //   solving the neighborhood.
354     IntegerValue initial_best_objective = IntegerValue(0);
355     IntegerValue base_objective = IntegerValue(0);
356     IntegerValue new_objective = IntegerValue(0);
357 
358     // Bounds data is only used by relaxation neighborhoods.
359     IntegerValue initial_best_objective_bound = IntegerValue(0);
360     IntegerValue new_objective_bound = IntegerValue(0);
361 
362     // This is just used to construct a deterministic order for the updates.
363     bool operator<(const SolveData& o) const {
364       return std::tie(status, difficulty, deterministic_limit,
365                       deterministic_time, initial_best_objective,
366                       base_objective, new_objective,
367                       initial_best_objective_bound, new_objective_bound,
368                       neighborhood_id) <
369              std::tie(o.status, o.difficulty, o.deterministic_limit,
370                       o.deterministic_time, o.initial_best_objective,
371                       o.base_objective, o.new_objective,
372                       o.initial_best_objective_bound, o.new_objective_bound,
373                       o.neighborhood_id);
374     }
375   };
AddSolveData(SolveData data)376   void AddSolveData(SolveData data) {
377     absl::MutexLock mutex_lock(&generator_mutex_);
378     solve_data_.push_back(data);
379   }
380 
381   // Process all the recently added solve data and update this generator
382   // score and difficulty.
383   void Synchronize();
384 
385   // Returns a short description of the generator.
name()386   std::string name() const { return name_; }
387 
388   // Number of times this generator was called.
num_calls()389   int64_t num_calls() const {
390     absl::MutexLock mutex_lock(&generator_mutex_);
391     return num_calls_;
392   }
393 
394   // Number of time the neighborhood was fully solved (OPTIMAL/INFEASIBLE).
num_fully_solved_calls()395   int64_t num_fully_solved_calls() const {
396     absl::MutexLock mutex_lock(&generator_mutex_);
397     return num_fully_solved_calls_;
398   }
399 
400   // The current difficulty of this generator
difficulty()401   double difficulty() const {
402     absl::MutexLock mutex_lock(&generator_mutex_);
403     return difficulty_.value();
404   }
405 
406   // The current time limit that the sub-solve should use on this generator.
deterministic_limit()407   double deterministic_limit() const {
408     absl::MutexLock mutex_lock(&generator_mutex_);
409     return deterministic_limit_;
410   }
411 
412   // The sum of the deterministic time spent in this generator.
deterministic_time()413   double deterministic_time() const {
414     absl::MutexLock mutex_lock(&generator_mutex_);
415     return deterministic_time_;
416   }
417 
418  protected:
419   // Triggered with each call to Synchronize() for each recently added
420   // SolveData. This is meant to be used for processing feedbacks by specific
421   // neighborhood generators to adjust the neighborhood generation process.
AdditionalProcessingOnSynchronize(const SolveData & solve_data)422   virtual void AdditionalProcessingOnSynchronize(const SolveData& solve_data) {}
423 
424   const std::string name_;
425   const NeighborhoodGeneratorHelper& helper_;
426   mutable absl::Mutex generator_mutex_;
427 
428  private:
429   std::vector<SolveData> solve_data_;
430 
431   // Current parameters to be used when generating/solving a neighborhood with
432   // this generator. Only updated on Synchronize().
433   AdaptiveParameterValue difficulty_;
434   double deterministic_limit_ = 0.1;
435 
436   // Current statistics of the last solved neighborhood.
437   // Only updated on Synchronize().
438   int64_t num_calls_ = 0;
439   int64_t num_fully_solved_calls_ = 0;
440   int64_t num_consecutive_non_improving_calls_ = 0;
441   double deterministic_time_ = 0.0;
442   double current_average_ = 0.0;
443 };
444 
445 // Pick a random subset of variables.
446 //
447 // TODO(user): In the presence of connected components, this should just work
448 // on one of them.
449 class RelaxRandomVariablesGenerator : public NeighborhoodGenerator {
450  public:
RelaxRandomVariablesGenerator(NeighborhoodGeneratorHelper const * helper,const std::string & name)451   explicit RelaxRandomVariablesGenerator(
452       NeighborhoodGeneratorHelper const* helper, const std::string& name)
453       : NeighborhoodGenerator(name, helper) {}
454   Neighborhood Generate(const CpSolverResponse& initial_solution,
455                         double difficulty, absl::BitGenRef random) final;
456 };
457 
458 // Pick a random subset of constraints and relax all the variables of these
459 // constraints. Note that to satisfy the difficulty, we might not relax all the
460 // variable of the "last" constraint.
461 //
462 // TODO(user): In the presence of connected components, this should just work
463 // on one of them.
464 class RelaxRandomConstraintsGenerator : public NeighborhoodGenerator {
465  public:
RelaxRandomConstraintsGenerator(NeighborhoodGeneratorHelper const * helper,const std::string & name)466   explicit RelaxRandomConstraintsGenerator(
467       NeighborhoodGeneratorHelper const* helper, const std::string& name)
468       : NeighborhoodGenerator(name, helper) {}
469   Neighborhood Generate(const CpSolverResponse& initial_solution,
470                         double difficulty, absl::BitGenRef random) final;
471 };
472 
473 // Pick a random subset of variables that are constructed by a BFS in the
474 // variable <-> constraint graph. That is, pick a random variable, then all the
475 // variable connected by some constraint to the first one, and so on. The
476 // variable of the last "level" are selected randomly.
477 //
478 // Note that in the presence of connected component, this works correctly
479 // already.
480 class VariableGraphNeighborhoodGenerator : public NeighborhoodGenerator {
481  public:
VariableGraphNeighborhoodGenerator(NeighborhoodGeneratorHelper const * helper,const std::string & name)482   explicit VariableGraphNeighborhoodGenerator(
483       NeighborhoodGeneratorHelper const* helper, const std::string& name)
484       : NeighborhoodGenerator(name, helper) {}
485   Neighborhood Generate(const CpSolverResponse& initial_solution,
486                         double difficulty, absl::BitGenRef random) final;
487 };
488 
489 // Pick a random subset of constraint and relax all of their variables. We are a
490 // bit smarter than this because after the first constraint is selected, we only
491 // select constraints that share at least one variable with the already selected
492 // constraints. The variable from the "last" constraint are selected randomly.
493 class ConstraintGraphNeighborhoodGenerator : public NeighborhoodGenerator {
494  public:
ConstraintGraphNeighborhoodGenerator(NeighborhoodGeneratorHelper const * helper,const std::string & name)495   explicit ConstraintGraphNeighborhoodGenerator(
496       NeighborhoodGeneratorHelper const* helper, const std::string& name)
497       : NeighborhoodGenerator(name, helper) {}
498   Neighborhood Generate(const CpSolverResponse& initial_solution,
499                         double difficulty, absl::BitGenRef random) final;
500 };
501 
502 // Helper method for the scheduling neighborhood generators. Returns the model
503 // as neighborhood for the given set of intervals to relax. For each no_overlap
504 // constraints, it adds strict relation order between the non-relaxed intervals.
505 Neighborhood GenerateSchedulingNeighborhoodForRelaxation(
506     const absl::Span<const int> intervals_to_relax,
507     const CpSolverResponse& initial_solution,
508     const NeighborhoodGeneratorHelper& helper);
509 
510 // Only make sense for scheduling problem. This select a random set of interval
511 // of the problem according to the difficulty. Then, for each no_overlap
512 // constraints, it adds strict relation order between the non-relaxed intervals.
513 //
514 // TODO(user): Also deal with cumulative constraint.
515 class SchedulingNeighborhoodGenerator : public NeighborhoodGenerator {
516  public:
SchedulingNeighborhoodGenerator(NeighborhoodGeneratorHelper const * helper,const std::string & name)517   explicit SchedulingNeighborhoodGenerator(
518       NeighborhoodGeneratorHelper const* helper, const std::string& name)
519       : NeighborhoodGenerator(name, helper) {}
520 
521   Neighborhood Generate(const CpSolverResponse& initial_solution,
522                         double difficulty, absl::BitGenRef random) final;
523 };
524 
525 // Similar to SchedulingNeighborhoodGenerator except the set of intervals that
526 // are relaxed are from a specific random time interval.
527 class SchedulingTimeWindowNeighborhoodGenerator : public NeighborhoodGenerator {
528  public:
SchedulingTimeWindowNeighborhoodGenerator(NeighborhoodGeneratorHelper const * helper,const std::string & name)529   explicit SchedulingTimeWindowNeighborhoodGenerator(
530       NeighborhoodGeneratorHelper const* helper, const std::string& name)
531       : NeighborhoodGenerator(name, helper) {}
532 
533   Neighborhood Generate(const CpSolverResponse& initial_solution,
534                         double difficulty, absl::BitGenRef random) final;
535 };
536 
537 // This routing based LNS generator will relax random arcs in all the paths of
538 // the circuit or routes constraints.
539 class RoutingRandomNeighborhoodGenerator : public NeighborhoodGenerator {
540  public:
RoutingRandomNeighborhoodGenerator(NeighborhoodGeneratorHelper const * helper,const std::string & name)541   RoutingRandomNeighborhoodGenerator(NeighborhoodGeneratorHelper const* helper,
542                                      const std::string& name)
543       : NeighborhoodGenerator(name, helper) {}
544 
545   Neighborhood Generate(const CpSolverResponse& initial_solution,
546                         double difficulty, absl::BitGenRef random) final;
547 };
548 
549 // This routing based LNS generator will relax small sequences of arcs randomly
550 // chosen in all the paths of the circuit or routes constraints.
551 class RoutingPathNeighborhoodGenerator : public NeighborhoodGenerator {
552  public:
RoutingPathNeighborhoodGenerator(NeighborhoodGeneratorHelper const * helper,const std::string & name)553   RoutingPathNeighborhoodGenerator(NeighborhoodGeneratorHelper const* helper,
554                                    const std::string& name)
555       : NeighborhoodGenerator(name, helper) {}
556 
557   Neighborhood Generate(const CpSolverResponse& initial_solution,
558                         double difficulty, absl::BitGenRef random) final;
559 };
560 
561 // This routing based LNS generator aims are relaxing one full path, and make
562 // some room on the other paths to absorb the nodes of the relaxed path.
563 //
564 // In order to do so, it will relax the first and the last arc of each path in
565 // the circuit or routes constraints. Then it will relax all arc literals in one
566 // random path. Then it will relax random arcs in the remaining paths until it
567 // reaches the given difficulty.
568 class RoutingFullPathNeighborhoodGenerator : public NeighborhoodGenerator {
569  public:
RoutingFullPathNeighborhoodGenerator(NeighborhoodGeneratorHelper const * helper,const std::string & name)570   RoutingFullPathNeighborhoodGenerator(
571       NeighborhoodGeneratorHelper const* helper, const std::string& name)
572       : NeighborhoodGenerator(name, helper) {}
573 
574   Neighborhood Generate(const CpSolverResponse& initial_solution,
575                         double difficulty, absl::BitGenRef random) final;
576 };
577 
578 // Generates a neighborhood by fixing the variables to solutions reported in
579 // various repositories. This is inspired from RINS published in "Exploring
580 // relaxation induced neighborhoods to improve MIP solutions" 2004 by E. Danna
581 // et.
582 //
583 // If incomplete_solutions is provided, this generates a neighborhood by fixing
584 // the variable values to a solution in the SharedIncompleteSolutionManager and
585 // ignores the other repositories.
586 //
587 // Otherwise, if response_manager is not provided, this generates a neighborhood
588 // using only the linear/general relaxation values. The domain of the variables
589 // are reduced to the integer values around their lp solution/relaxation
590 // solution values. This was published in "RENS – The Relaxation Enforced
591 // Neighborhood" 2009 by Timo Berthold.
592 class RelaxationInducedNeighborhoodGenerator : public NeighborhoodGenerator {
593  public:
RelaxationInducedNeighborhoodGenerator(NeighborhoodGeneratorHelper const * helper,const SharedResponseManager * response_manager,const SharedRelaxationSolutionRepository * relaxation_solutions,const SharedLPSolutionRepository * lp_solutions,SharedIncompleteSolutionManager * incomplete_solutions,const std::string & name)594   explicit RelaxationInducedNeighborhoodGenerator(
595       NeighborhoodGeneratorHelper const* helper,
596       const SharedResponseManager* response_manager,
597       const SharedRelaxationSolutionRepository* relaxation_solutions,
598       const SharedLPSolutionRepository* lp_solutions,
599       SharedIncompleteSolutionManager* incomplete_solutions,
600       const std::string& name)
601       : NeighborhoodGenerator(name, helper),
602         response_manager_(response_manager),
603         relaxation_solutions_(relaxation_solutions),
604         lp_solutions_(lp_solutions),
605         incomplete_solutions_(incomplete_solutions) {
606     CHECK(lp_solutions_ != nullptr || relaxation_solutions_ != nullptr ||
607           incomplete_solutions != nullptr);
608   }
609 
610   // Both initial solution and difficulty values are ignored.
611   Neighborhood Generate(const CpSolverResponse& initial_solution,
612                         double difficulty, absl::BitGenRef random) final;
613 
614   // Returns true if the required solutions are available.
615   bool ReadyToGenerate() const override;
616 
617  private:
618   const SharedResponseManager* response_manager_;
619   const SharedRelaxationSolutionRepository* relaxation_solutions_;
620   const SharedLPSolutionRepository* lp_solutions_;
621   SharedIncompleteSolutionManager* incomplete_solutions_;
622 };
623 
624 // Generates a relaxation of the original model by removing a consecutive span
625 // of constraints starting at a random index. The number of constraints removed
626 // is in sync with the difficulty passed to the generator.
627 class ConsecutiveConstraintsRelaxationNeighborhoodGenerator
628     : public NeighborhoodGenerator {
629  public:
ConsecutiveConstraintsRelaxationNeighborhoodGenerator(NeighborhoodGeneratorHelper const * helper,const std::string & name)630   explicit ConsecutiveConstraintsRelaxationNeighborhoodGenerator(
631       NeighborhoodGeneratorHelper const* helper, const std::string& name)
632       : NeighborhoodGenerator(name, helper) {}
633   Neighborhood Generate(const CpSolverResponse& initial_solution,
634                         double difficulty, absl::BitGenRef random) final;
635 
IsRelaxationGenerator()636   bool IsRelaxationGenerator() const override { return true; }
ReadyToGenerate()637   bool ReadyToGenerate() const override { return true; }
638 };
639 
640 // Generates a relaxation of the original model by removing some constraints
641 // randomly with a given weight for each constraint that controls the
642 // probability of constraint getting removed. The number of constraints removed
643 // is in sync with the difficulty passed to the generator. Higher weighted
644 // constraints are more likely to get removed.
645 class WeightedRandomRelaxationNeighborhoodGenerator
646     : public NeighborhoodGenerator {
647  public:
648   WeightedRandomRelaxationNeighborhoodGenerator(
649       NeighborhoodGeneratorHelper const* helper, const std::string& name);
650 
651   // Generates the neighborhood as described above. Also stores the removed
652   // constraints indices for adjusting the weights.
653   Neighborhood Generate(const CpSolverResponse& initial_solution,
654                         double difficulty, absl::BitGenRef random) final;
655 
IsRelaxationGenerator()656   bool IsRelaxationGenerator() const override { return true; }
ReadyToGenerate()657   bool ReadyToGenerate() const override { return true; }
658 
659  private:
660   // Adjusts the weights of the constraints removed to get the neighborhood
661   // based on the solve_data.
662   void AdditionalProcessingOnSynchronize(const SolveData& solve_data) override
663       ABSL_EXCLUSIVE_LOCKS_REQUIRED(generator_mutex_);
664 
665   // Higher weighted constraints are more likely to get removed.
666   std::vector<double> constraint_weights_;
667   int num_removable_constraints_ = 0;
668 
669   // Indices of the removed constraints per generated neighborhood.
670   absl::flat_hash_map<int64_t, std::vector<int>> removed_constraints_
671       ABSL_GUARDED_BY(generator_mutex_);
672 
673   // TODO(user): Move this to parent class if other generators start using
674   // feedbacks.
675   int64_t next_available_id_ ABSL_GUARDED_BY(generator_mutex_) = 0;
676 };
677 
678 }  // namespace sat
679 }  // namespace operations_research
680 
681 #endif  // OR_TOOLS_SAT_CP_MODEL_LNS_H_
682