OR-Tools  9.3
gurobi_solver.h
Go to the documentation of this file.
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_MATH_OPT_SOLVERS_GUROBI_SOLVER_H_
15#define OR_TOOLS_MATH_OPT_SOLVERS_GUROBI_SOLVER_H_
16
17#include <cstdint>
18#include <limits>
19#include <memory>
20#include <optional>
21#include <string>
22#include <utility>
23#include <vector>
24
25#include "absl/status/status.h"
26#include "absl/status/statusor.h"
27#include "absl/strings/string_view.h"
28#include "absl/time/time.h"
29#include "absl/types/span.h"
33#include "ortools/math_opt/callback.pb.h"
37#include "ortools/math_opt/model.pb.h"
38#include "ortools/math_opt/model_parameters.pb.h"
39#include "ortools/math_opt/model_update.pb.h"
40#include "ortools/math_opt/parameters.pb.h"
41#include "ortools/math_opt/result.pb.h"
42#include "ortools/math_opt/solution.pb.h"
46#include "ortools/math_opt/sparse_containers.pb.h"
47
48namespace operations_research {
49namespace math_opt {
50
52 public:
53 static absl::StatusOr<std::unique_ptr<GurobiSolver>> New(
54 const ModelProto& input_model,
55 const SolverInterface::InitArgs& init_args);
56
57 absl::StatusOr<SolveResultProto> Solve(
58 const SolveParametersProto& parameters,
59 const ModelSolveParametersProto& model_parameters,
60 MessageCallback message_cb,
61 const CallbackRegistrationProto& callback_registration, Callback cb,
62 SolveInterrupter* interrupter) override;
63 absl::Status Update(const ModelUpdateProto& model_update) override;
64 bool CanUpdate(const ModelUpdateProto& model_update) override;
65
66 private:
67 struct GurobiCallbackData {
68 explicit GurobiCallbackData(GurobiCallbackInput callback_input,
69 SolveInterrupter* const local_interrupter)
70 : callback_input(std::move(callback_input)),
71 local_interrupter(local_interrupter) {}
72 const GurobiCallbackInput callback_input;
73
74 // Interrupter triggered when either the user interrupter passed to Solve()
75 // is triggered or after one user callback returned a true `terminate`.
76 //
77 // This is not the user interrupter though so it safe for callbacks to
78 // trigger it.
79 //
80 // It is optional; it is not null when either we have a LP/MIP callback or a
81 // user interrupter. But it can be null if we only have a message callback.
82 SolveInterrupter* const local_interrupter;
83
84 MessageCallbackData message_callback_data;
85
86 absl::Status status = absl::OkStatus();
87 };
88
89 explicit GurobiSolver(std::unique_ptr<Gurobi> g_gurobi);
90
91 // For easing reading the code, we declare these types:
92 using VariableId = int64_t;
93 using LinearConstraintId = int64_t;
94 using GurobiVariableIndex = int;
95 using GurobiLinearConstraintIndex = int;
96
97 static constexpr GurobiVariableIndex kUnspecifiedIndex = -1;
98 static constexpr GurobiLinearConstraintIndex kUnspecifiedConstraint = -2;
99 static constexpr double kInf = std::numeric_limits<double>::infinity();
100 // Data associated with each constraint. With it we know if the underlying
101 // representation is either:
102 // linear_terms <= upper_bound (if lower bound <= -GRB_INFINITY)
103 // linear_terms >= lower_bound (if upper bound >= GRB_INFINTY)
104 // linear_terms == xxxxx_bound (if upper_bound == lower_bound)
105 // linear_term - slack == 0 (with slack bounds equal to xxxxx_bound)
106 struct ConstraintData {
107 GurobiLinearConstraintIndex constraint_index = kUnspecifiedConstraint;
108 // only valid for true ranged constraints.
109 GurobiVariableIndex slack_index = kUnspecifiedIndex;
110 double lower_bound = -kInf;
111 double upper_bound = kInf;
112 };
113
114 struct SlackInfo {
115 LinearConstraintId id;
116 ConstraintData& constraint_data;
117 SlackInfo(const LinearConstraintId input_id,
118 ConstraintData& input_constraint)
119 : id(input_id), constraint_data(input_constraint) {}
120 };
121
122 struct SolutionClaims {
123 bool primal_feasible_solution_exists;
124 bool dual_feasible_solution_exists;
125 };
126
127 struct SolutionsAndClaims {
128 std::vector<SolutionProto> solutions;
129 SolutionClaims solution_claims;
130 };
131
132 template <typename SolutionType>
133 struct SolutionAndClaim {
134 std::optional<SolutionType> solution;
135 bool feasible_solution_exists = false;
136 };
137
139
140 absl::StatusOr<ProblemStatusProto> GetProblemStatus(
141 const int grb_termination, const SolutionClaims solution_claims);
142 absl::StatusOr<SolveResultProto> ExtractSolveResultProto(
143 absl::Time start, const ModelSolveParametersProto& model_parameters);
144 absl::Status FillRays(const ModelSolveParametersProto& model_parameters,
145 SolveResultProto& result);
146 absl::StatusOr<GurobiSolver::SolutionsAndClaims> GetSolutions(
147 const ModelSolveParametersProto& model_parameters);
148 absl::StatusOr<SolveStatsProto> GetSolveStats(absl::Time start,
149 SolutionClaims solution_claims);
150
151 absl::StatusOr<double> GetBestDualBound();
152 absl::StatusOr<double> GetBestPrimalBound(bool has_primal_feasible_solution);
153 bool PrimalSolutionQualityAvailable() const;
154 absl::StatusOr<double> GetPrimalSolutionQuality() const;
155
156 // Warning: is read from gurobi, take care with gurobi update.
157 absl::StatusOr<bool> IsMaximize() const;
158
159 static absl::StatusOr<TerminationProto> ConvertTerminationReason(
160 int gurobi_status, SolutionClaims solution_claims);
161
162 absl::StatusOr<SolutionsAndClaims> GetQpSolution(
163 const ModelSolveParametersProto& model_parameters);
164 absl::StatusOr<SolutionsAndClaims> GetLpSolution(
165 const ModelSolveParametersProto& model_parameters);
166 absl::StatusOr<SolutionsAndClaims> GetMipSolutions(
167 const ModelSolveParametersProto& model_parameters);
168
169 // return bool field should be true if a primal solution exists.
170 absl::StatusOr<SolutionAndClaim<PrimalSolutionProto>>
171 GetConvexPrimalSolutionIfAvailable(
172 const ModelSolveParametersProto& model_parameters);
173 absl::StatusOr<SolutionAndClaim<DualSolutionProto>>
174 GetLpDualSolutionIfAvailable(
175 const ModelSolveParametersProto& model_parameters);
176 absl::StatusOr<std::optional<BasisProto>> GetBasisIfAvailable();
177
178 absl::Status SetParameters(const SolveParametersProto& parameters);
179 absl::Status AddNewConstraints(const LinearConstraintsProto& constraints);
180 absl::Status AddNewVariables(const VariablesProto& new_variables);
181 absl::Status AddNewSlacks(const std::vector<SlackInfo>& new_slacks);
182 absl::Status ChangeCoefficients(const SparseDoubleMatrixProto& matrix);
183 // NOTE: Clears any existing quadratic objective terms.
184 absl::Status ResetQuadraticObjectiveTerms(
185 const SparseDoubleMatrixProto& terms);
186 // Updates objective so that it is the sum of everything in terms, plus all
187 // other terms prexisting in the objective that are not overwritten by terms.
188 absl::Status UpdateQuadraticObjectiveTerms(
189 const SparseDoubleMatrixProto& terms);
190 absl::Status LoadModel(const ModelProto& input_model);
191
192 absl::Status UpdateDoubleListAttribute(const SparseDoubleVectorProto& update,
193 const char* attribute_name,
194 const IdHashMap& id_hash_map);
195 absl::Status UpdateInt32ListAttribute(const SparseInt32VectorProto& update,
196 const char* attribute_name,
197 const IdHashMap& id_hash_map);
198 absl::Status UpdateGurobiIndices();
199 absl::Status UpdateLinearConstraints(
200 const LinearConstraintUpdatesProto& update,
201 std::vector<GurobiVariableIndex>& deleted_variables_index);
202
203 int num_gurobi_constraints() const;
204 int get_model_index(GurobiVariableIndex index) const { return index; }
205 int get_model_index(const ConstraintData& index) const {
206 return index.constraint_index;
207 }
208
209 // Fills in result with the values in gurobi_values aided by the index
210 // conversion from map which should be either variables_map_ or
211 // linear_constraints_map_ as appropriate. Only key/value pairs that passes
212 // the filter predicate are added.
213 template <typename T>
214 void GurobiVectorToSparseDoubleVector(
215 absl::Span<const double> gurobi_values, const T& map,
216 SparseDoubleVectorProto& result,
217 const SparseVectorFilterProto& filter) const;
218 absl::StatusOr<BasisProto> GetGurobiBasis();
219 absl::Status SetGurobiBasis(const BasisProto& basis);
220 absl::StatusOr<DualRayProto> GetGurobiDualRay(
221 const SparseVectorFilterProto& linear_constraints_filter,
222 const SparseVectorFilterProto& variables_filter, bool is_maximize);
223 absl::StatusOr<bool> IsLP() const;
224 absl::StatusOr<bool> IsQP() const;
225
226 absl::StatusOr<std::unique_ptr<GurobiCallbackData>> RegisterCallback(
227 const CallbackRegistrationProto& registration, Callback cb,
228 const MessageCallback message_cb, absl::Time start,
229 SolveInterrupter* interrupter);
230
231 // Returns the ids of variables and linear constraints with inverted bounds.
232 absl::StatusOr<InvertedBounds> ListInvertedBounds() const;
233
234 const std::unique_ptr<Gurobi> gurobi_;
235
236 // Note that we use linked_hash_map because the index of the gurobi_model_
237 // variables/constraints is exactly the order in which they are added to the
238 // model.
239 // Internal correspondence from variable proto IDs to Gurobi-numbered
240 // variables.
242 // Internal correspondence from linear constraint proto IDs to
243 // Gurobi-numbered linear constraint and extra information.
245 linear_constraints_map_;
246 // For those constraints that need a slack in the gurobi_model_
247 // representation (i.e. those with
248 // ConstraintData.slack_index != kUnspecifiedIndex), we keep a hash_map of
249 // those LinearConstraintId's to a reference of the ConstraintData stored in
250 // the linear_constraints_map_. This means that we only have one location
251 // were we store information about constraints, but two places from where we
252 // can access it.
253 // Furthermore, the internal slack_index associated with each
254 // actual slack variable is increasing in the order of them in this
255 // slack_map_, and is the main reason why we choose to use
256 // gtl::linked_hash_map, as the order of insertion (of the remaining
257 // elements) is preserved after removals.
258 // Note that after deletions, the renumbering of variables must be done
259 // simultaneously for variables and slacks following the merged-order of the
260 // variable_index and slack_map_.slack_index.
262 // Number of Gurobi variables, this internal quantity is updated when we
263 // actually add or remove variables or constraints in the underlying Gurobi
264 // model. Furthermore, since 'ModelUpdate' can trigger both creation and
265 // deletion of variables and constraints, we batch those changes in two
266 // steps: first add all new variables and constraints, and then delete all
267 // variables and constraints that need deletion. Finally flush changes at
268 // the gurobi model level (if any deletion was performed).
269 int num_gurobi_variables_ = 0;
270 // Gurobi does not expose a way to query quadratic objective terms from the
271 // model, so we track them. Notes:
272 // * Keys are in upper triangular order (.first <= .second)
273 // * Terms not in the map have zero coefficients
274 // Note also that the map may also have entries with zero coefficient value.
275 absl::flat_hash_map<std::pair<VariableId, VariableId>, double>
276 quadratic_objective_coefficients_;
277
278 static constexpr int kGrbBasicConstraint = 0;
279 static constexpr int kGrbNonBasicConstraint = -1;
280};
281
282} // namespace math_opt
283} // namespace operations_research
284
285#endif // OR_TOOLS_MATH_OPT_SOLVERS_GUROBI_SOLVER_H_
bool CanUpdate(const ModelUpdateProto &model_update) override
absl::Status Update(const ModelUpdateProto &model_update) override
static absl::StatusOr< std::unique_ptr< GurobiSolver > > New(const ModelProto &input_model, const SolverInterface::InitArgs &init_args)
absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto &parameters, const ModelSolveParametersProto &model_parameters, MessageCallback message_cb, const CallbackRegistrationProto &callback_registration, Callback cb, SolveInterrupter *interrupter) override
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
SatParameters parameters
absl::Status status
Definition: g_gurobi.cc:35
double upper_bound
double lower_bound
int index
Collection of objects used to extend the Constraint Solver library.
int64_t start