bop_lns.h 7.47 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
// Copyright 2010-2018 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef OR_TOOLS_BOP_BOP_LNS_H_
#define OR_TOOLS_BOP_BOP_LNS_H_

#include <string>
#include <vector>

#include "ortools/base/basictypes.h"
#include "ortools/base/int_type.h"
#include "ortools/base/int_type_indexed_vector.h"
#include "ortools/base/integral_types.h"
#include "ortools/base/logging.h"
#include "ortools/base/macros.h"
#include "ortools/base/random.h"
#include "ortools/bop/bop_base.h"
#include "ortools/bop/bop_parameters.pb.h"
#include "ortools/bop/bop_solution.h"
#include "ortools/bop/bop_types.h"
#include "ortools/bop/bop_util.h"
#include "ortools/glop/lp_solver.h"
#include "ortools/sat/boolean_problem.pb.h"
#include "ortools/sat/sat_solver.h"
#include "ortools/util/stats.h"
#include "ortools/util/time_limit.h"

namespace operations_research {
namespace bop {

// Uses SAT to solve the full problem under the constraint that the new solution
// should be under a given Hamming distance of the current solution.
class BopCompleteLNSOptimizer : public BopOptimizerBase {
 public:
  BopCompleteLNSOptimizer(const std::string& name,
                          const BopConstraintTerms& objective_terms);
  ~BopCompleteLNSOptimizer() final;

 private:
  bool ShouldBeRun(const ProblemState& problem_state) const final;
  Status Optimize(const BopParameters& parameters,
                  const ProblemState& problem_state, LearnedInfo* learned_info,
                  TimeLimit* time_limit) final;

  BopOptimizerBase::Status SynchronizeIfNeeded(
      const ProblemState& problem_state, int num_relaxed_vars);

  int64 state_update_stamp_;
  std::unique_ptr<sat::SatSolver> sat_solver_;
  const BopConstraintTerms& objective_terms_;
};

// Interface of the different LNS neighborhood generation algorithm.
//
// NOTE(user): Using a sat_propagator as the output of the algorithm allows for
// a really simple and efficient interface for the generator that relies on it.
// However, if a generator doesn't rely on it at all, it may slow down a bit the
// code (to investigate). If this happens, we will probably need another
// function here and a way to select between which one to call.
class NeighborhoodGenerator {
 public:
  NeighborhoodGenerator() {}
  virtual ~NeighborhoodGenerator() {}

  // Interface for the neighborhood generation.
  //
  // The difficulty will be in [0, 1] and is related to the asked neighborhood
  // size (and thus local problem difficulty). A difficulty of 0.0 means empty
  // neighborhood and a difficulty of 1.0 means the full problem. The algorithm
  // should try to generate an neighborhood according to this difficulty, which
  // will be dynamically adjusted depending on whether or not we can solve the
  // subproblem.
  //
  // The given sat_propagator will be reset and then configured so that all the
  // variables propagated on its trail should be fixed. That is, the
  // neighborhood will correspond to the unassigned variables in the
  // sat_propagator. Note that sat_propagator_.IsModelUnsat() will be checked
  // after this is called so it is okay to abort if this happens.
  //
  // Preconditions:
  // - The given sat_propagator_ should have the current problem loaded (with
  //   the constraint to find a better solution that any current solution).
  // - The problem state must contains a feasible solution.
  virtual void GenerateNeighborhood(const ProblemState& problem_state,
                                    double difficulty,
                                    sat::SatSolver* sat_propagator) = 0;
};

// A generic LNS optimizer which generates neighborhoods according to the given
// NeighborhoodGenerator and automatically adapt the neighborhood size depending
// on how easy it is to solve the associated problem.
class BopAdaptiveLNSOptimizer : public BopOptimizerBase {
 public:
  // Takes ownership of the given neighborhood_generator.
  // The sat_propagator is assumed to contains the current problem.
  BopAdaptiveLNSOptimizer(const std::string& name, bool use_lp_to_guide_sat,
                          NeighborhoodGenerator* neighborhood_generator,
                          sat::SatSolver* sat_propagator);
  ~BopAdaptiveLNSOptimizer() final;

 private:
  bool ShouldBeRun(const ProblemState& problem_state) const final;
  Status Optimize(const BopParameters& parameters,
                  const ProblemState& problem_state, LearnedInfo* learned_info,
                  TimeLimit* time_limit) final;

  const bool use_lp_to_guide_sat_;
  std::unique_ptr<NeighborhoodGenerator> neighborhood_generator_;
  sat::SatSolver* const sat_propagator_;

  // Adaptive neighborhood size logic.
  // The values are kept from one run to the next.
  LubyAdaptiveParameterValue adaptive_difficulty_;
};

// Generates a neighborhood by randomly fixing a subset of the objective
// variables that are currently at their lower cost.
class ObjectiveBasedNeighborhood : public NeighborhoodGenerator {
 public:
  ObjectiveBasedNeighborhood(const BopConstraintTerms* objective_terms,
                             MTRandom* random)
      : objective_terms_(*objective_terms), random_(random) {}
  ~ObjectiveBasedNeighborhood() final {}

 private:
  void GenerateNeighborhood(const ProblemState& problem_state,
                            double difficulty,
                            sat::SatSolver* sat_propagator) final;
  const BopConstraintTerms& objective_terms_;
  MTRandom* random_;
};

// Generates a neighborhood by randomly selecting a subset of constraints and
// fixing the objective variables that are currently at their lower cost and
// not in the given subset of constraints.
class ConstraintBasedNeighborhood : public NeighborhoodGenerator {
 public:
  ConstraintBasedNeighborhood(const BopConstraintTerms* objective_terms,
                              MTRandom* random)
      : objective_terms_(*objective_terms), random_(random) {}
  ~ConstraintBasedNeighborhood() final {}

 private:
  void GenerateNeighborhood(const ProblemState& problem_state,
                            double difficulty,
                            sat::SatSolver* sat_propagator) final;
  const BopConstraintTerms& objective_terms_;
  MTRandom* random_;
};

// Generates a neighborhood by taking a random local neighborhood in an
// undirected graph where the nodes are the variables and two nodes are linked
// if they appear in the same constraint.
class RelationGraphBasedNeighborhood : public NeighborhoodGenerator {
 public:
  RelationGraphBasedNeighborhood(const LinearBooleanProblem& problem,
                                 MTRandom* random);
  ~RelationGraphBasedNeighborhood() final {}

 private:
  void GenerateNeighborhood(const ProblemState& problem_state,
                            double difficulty,
                            sat::SatSolver* sat_propagator) final;

  // TODO(user): reuse by_variable_matrix_ from the LS? Note however than we
  // don't need the coefficients here.
  gtl::ITIVector<VariableIndex, std::vector<ConstraintIndex>> columns_;
  MTRandom* random_;
};

}  // namespace bop
}  // namespace operations_research
#endif  // OR_TOOLS_BOP_BOP_LNS_H_