linear_solver_callback.h 6.75 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
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
// 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.

// See go/mpsolver-callbacks for documentation on how to use this file.

#ifndef OR_TOOLS_LINEAR_SOLVER_LINEAR_SOLVER_CALLBACK_H_
#define OR_TOOLS_LINEAR_SOLVER_LINEAR_SOLVER_CALLBACK_H_

#include <string>

#include "absl/container/flat_hash_map.h"
#include "ortools/base/integral_types.h"

namespace operations_research {

class MPVariable;
class LinearExpr;
class LinearRange;

// The current state of the solver when the callback is invoked.
//
// For Gurobi, similar to the int 'where' in the Gurobi callback API.
// See http://www.gurobi.com/documentation/8.0/refman/callback_codes.html
// for details.
enum class MPCallbackEvent {
  kUnknown,
  // For regaining control of the main thread in single threaded applications,
  // not for interacting with the solver.
  kPolling,
  // The solver is currently running presolve.
  kPresolve,
  // The solver is currently running the simplex method.
  kSimplex,
  // The solver is in the MIP loop (called periodically before starting a new
  // node).  Useful to early termination.
  kMip,
  // Called every time a new MIP incumbent is found.
  kMipSolution,
  // Called once per pass of the cut loop inside each MIP node.
  kMipNode,
  // Called in each iterate of IPM/barrier method.
  kBarrier,
  // The solver is about to log out a message, use this callback to capture it.
  kMessage,
  // The solver is in multi-objective optimization.
  kMultiObj,
};

std::string ToString(MPCallbackEvent event);

// When querying solution values or modifying the model during a callback, use
// this API, rather than manipulating MPSolver directly.  You should only
// interact with this object from within MPCallback::RunCallback().
class MPCallbackContext {
 public:
  virtual ~MPCallbackContext() {}

  // What the solver is currently doing.  How you can interact with the solver
  // from the callback depends on this value.
  virtual MPCallbackEvent Event() = 0;

  // Always false if event is not kMipSolution or kMipNode, otherwise behavior
  // may be solver dependent.
  //
  // For Gurobi, under kMipNode, may be false if the node was not solved to
  // optimality, see MIPNODE_REL here for details:
  // http://www.gurobi.com/documentation/8.0/refman/callback_codes.html
  virtual bool CanQueryVariableValues() = 0;

  // Returns the value of variable from the solver's current state.
  //
  // Call only when CanQueryVariableValues() is true.
  //
  // At kMipSolution, the solution is integer feasible, while at kMipNode, the
  // solution solves the current node's LP relaxation (so integer variables may
  // be fractional).
  virtual double VariableValue(const MPVariable* variable) = 0;

  // Adds a constraint to the model that strengths the LP relaxation.
  //
  // Call only when the event is kMipNode.
  //
  // Requires that MPCallback::might_add_cuts() is true.
  //
  // This constraint must not cut off integer solutions, it should only
  // strengthen the LP (behavior is undefined otherwise).  Use
  // MPCallbackContext::AddLazyConstriant() if you are cutting off integer
  // solutions.
  virtual void AddCut(const LinearRange& cutting_plane) = 0;

  // Adds a constraint to the model that cuts off an undesired integer solution.
  //
  // Call only when the event is kMipSolution or kMipNode.
  //
  // Requires that MPCallback::might_add_lazy_constraints() is true.
  //
  // Use this to avoid adding a large number of constraints to the model where
  // most are expected to not be needed.
  //
  // Given an integral solution, AddLazyConstraint() MUST be able to detect if
  // there is a violated constraint, and it is guaranteed that every integer
  // solution will be checked by AddLazyConstraint().
  //
  // Warning(rander): in some solvers, e.g. Gurobi, an integer solution may not
  // respect a previously added lazy constraint, so you may need to add a
  // constraint more than once (e.g. due to threading issues).
  virtual void AddLazyConstraint(const LinearRange& lazy_constraint) = 0;

  // Suggests a (potentially partial) variable assignment to the solver, to be
  // used as a feasible solution (or part of one). If the assignment is partial,
  // certain solvers (e.g. Gurobi) will try to compute a feasible solution from
  // the partial assignment. Returns the objective value of the solution if the
  // solver supports it.
  //
  // Call only when the event is kMipNode.
  virtual double SuggestSolution(
      const absl::flat_hash_map<const MPVariable*, double>& solution) = 0;

  // Returns the number of nodes explored so far in the branch and bound tree,
  // which 0 at the root node and > 0 otherwise.
  //
  // Call only when the event is kMipSolution or kMipNode.
  virtual int64 NumExploredNodes() = 0;
};

// Extend this class with model specific logic, and register through
// MPSolver::SetCallback, passing a pointer to this object.
//
// See go/mpsolver-callbacks for additional documentation.
class MPCallback {
 public:
  // If you intend to call call MPCallbackContext::AddCut(), you must set
  // might_add_cuts below to be true.  Likewise for
  // MPCallbackContext::AddLazyConstraint() and might_add_lazy_constraints.
  MPCallback(bool might_add_cuts, bool might_add_lazy_constraints)
      : might_add_cuts_(might_add_cuts),
        might_add_lazy_constraints_(might_add_lazy_constraints) {}
  virtual ~MPCallback() {}

  // Threading behavior may be solver dependent:
  //   * Gurobi: RunCallback always runs on the same thread that you called
  //     MPSolver::Solve() on, even when Gurobi uses multiple threads.
  virtual void RunCallback(MPCallbackContext* callback_context) = 0;

  bool might_add_cuts() const { return might_add_cuts_; }
  bool might_add_lazy_constraints() const {
    return might_add_lazy_constraints_;
  }

 private:
  bool might_add_cuts_;
  bool might_add_lazy_constraints_;
};

// Single callback that runs the list of callbacks given at construction, in
// sequence.
class MPCallbackList : public MPCallback {
 public:
  explicit MPCallbackList(const std::vector<MPCallback*>& callbacks);

  // Runs all callbacks from the list given at construction, in sequence.
  void RunCallback(MPCallbackContext* context) override;

 private:
  const std::vector<MPCallback*> callbacks_;
};

}  // namespace operations_research

#endif  // OR_TOOLS_LINEAR_SOLVER_LINEAR_SOLVER_CALLBACK_H_