OR-Tools  9.2
math_opt.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// A C++ API for building optimization problems.
15//
16// Warning: Variable, LinearConstraint, and Objective are value types, see
17// "Memory Model" below.
18//
19// A simple example:
20//
21// Model the problem:
22// max 2.0 * x + y
23// s.t. x + y <= 1.5
24// x in {0.0, 1.0}
25// y in [0.0, 2.5]
26//
27// using ::operations_research::math_opt::LinearConstraint;
28// using ::operations_research::math_opt::Objective;
29// using ::operations_research::math_opt::MathOpt;
30// using ::operations_research::math_opt::Result;
31// using ::operations_research::math_opt::SolveParameters;
32// using ::operations_research::math_opt::SolveResultProto;
33// using ::operations_research::math_opt::Variable;
34//
35// Version 1:
36//
37// MathOpt optimizer(operations_research::math_opt::SOLVER_TYPE_GSCIP,
38// "my_model");
39// const Variable x = optimizer.AddBinaryVariable("x");
40// const Variable y = optimizer.AddContinuousVariable(0.0, 2.5, "y");
41// const LinearConstraint c = optimizer.AddLinearConstraint(
42// -std::numeric_limits<double>::infinity(), 1.5, "c");
43// c.set_coefficient(x, 1.0);
44// c.set_coefficient(y, 1.0);
45// const Objective obj = optimizer.objective();
46// obj.set_linear_coefficient(x, 2.0);
47// obj.set_linear_coefficient(y, 1.0);
48// obj.set_maximize();
49// const Result result = optimizer.Solve(SolveParametersProto()).value();
50// for (const auto& warning : result.warnings) {
51// std::cerr << "Solver warning: " << warning << std::endl;
52// }
53// CHECK_EQ(result.termination_reason, SolveResultProto::OPTIMAL)
54// << result.termination_detail;
55// // The following code will print:
56// // objective value: 2.5
57// // value for variable x: 1
58// std::cout << "objective value: " << result.objective_value()
59// << "\nvalue for variable x: " << result.variable_values().at(x)
60// << std::endl;
61//
62// Version 2 (with linear expressions):
63//
64// MathOpt optimizer(operations_research::math_opt::SOLVER_TYPE_GSCIP,
65// "my_model");
66// const Variable x = optimizer.AddBinaryVariable("x");
67// const Variable y = optimizer.AddContinuousVariable(0.0, 2.5, "y");
68// // We can directly use linear combinations of variables ...
69// optimizer.AddLinearConstraint(x + y <= 1.5, "c");
70// // ... or build them incrementally.
71// LinearExpression objective_expression;
72// objective_expression += 2*x;
73// objective_expression += y;
74// optimizer.objective().Maximize(objective_expression);
75// const Result result = optimizer.Solve(SolveParametersProto()).value();
76// for (const auto& warning : result.warnings) {
77// std::cerr << "Solver warning: " << warning << std::endl;
78// }
79// CHECK_EQ(result.termination_reason, SolveResultProto::OPTIMAL)
80// << result.termination_detail;
81// // The following code will print:
82// // objective value: 2.5
83// // value for variable x: 1
84// std::cout << "objective value: " << result.objective_value()
85// << "\nvalue for variable x: " << result.variable_values().at(x)
86// << std::endl;
87//
88// Memory model:
89//
90// Variable, LinearConstraint, and Objective are all value types that
91// represent references to the underlying MathOpt object. They don't hold any of
92// the actual model data, they can be copied, and they should be passed by
93// value. They can be regenerated arbitrarily from MathOpt. MathOpt holds all
94// the data.
95//
96// Performance:
97//
98// This class is a thin wrapper around IndexedModel (for incrementally building
99// the model and reading it back, and producing the Model proto) and Solver (for
100// consuming the Model proto to solve the optimization problem). Operations for
101// building/reading/modifying the problem typically run in O(read/write size)
102// and rely on hashing, see the indexed model documentation for details. At
103// solve time (if you are solving locally) beware that there will be (at least)
104// three copies of the model in memory, IndexedModel, the Model proto, and the
105// underlying solver's copy(/ies). Note that the Model proto is reclaimed before
106// the underlying solver begins solving.
107
108#ifndef OR_TOOLS_MATH_OPT_CPP_MATH_OPT_H_
109#define OR_TOOLS_MATH_OPT_CPP_MATH_OPT_H_
110
111#include <functional>
112#include <memory>
113#include <string>
114#include <utility>
115#include <vector>
116
117#include "ortools/base/logging.h"
118#include "absl/memory/memory.h"
119#include "absl/status/statusor.h"
120#include "absl/strings/string_view.h"
123#include "ortools/math_opt/cpp/callback.h" // IWYU pragma: export
124#include "ortools/math_opt/cpp/linear_constraint.h" // IWYU pragma: export
125#include "ortools/math_opt/cpp/model_solve_parameters.h" // IWYU pragma: export
126#include "ortools/math_opt/cpp/objective.h" // IWYU pragma: export
127#include "ortools/math_opt/cpp/result.h" // IWYU pragma: export
128#include "ortools/math_opt/cpp/variable_and_expressions.h" // IWYU pragma: export
129#include "ortools/math_opt/model.pb.h" // IWYU pragma: export
130#include "ortools/math_opt/parameters.pb.h" // IWYU pragma: export
131#include "ortools/math_opt/result.pb.h" // IWYU pragma: export
132
133namespace operations_research {
134namespace math_opt {
135
136// Models and solves mathematical optimization problems.
137class MathOpt {
138 public:
139 using Callback = std::function<CallbackResult(CallbackData)>;
140
141 MathOpt(const MathOpt&) = delete;
142 MathOpt& operator=(const MathOpt&) = delete;
143
144 // Creates an empty minimization problem.
145 inline explicit MathOpt(
146 SolverType solver_type, absl::string_view name = "",
147 SolverInitializerProto solver_initializer = SolverInitializerProto());
148
149 inline const std::string& name() const;
150
151 // Adds a variable to the model and returns a reference to it.
152 inline Variable AddVariable(double lower_bound, double upper_bound,
153 bool is_integer, absl::string_view name = "");
154
155 // Adds a continuous unbounded variable to the model.
156 inline Variable AddVariable(absl::string_view name = "");
157
158 // Adds an variable to the model with domain {0, 1}.
159 inline Variable AddBinaryVariable(absl::string_view name = "");
160
161 // Adds a variable to the model with domain [lower_bound, upper_bound].
163 absl::string_view name = "");
164
165 // Adds a variable to the model that can take integer values between
166 // lower_bound and upper_bound (inclusive).
168 absl::string_view name = "");
169
170 // Removes a variable from the model.
171 //
172 // It is an error to use any reference to this variable after this operation.
173 // Runs in O(#constraints containing the variable).
174 inline void DeleteVariable(Variable variable);
175
176 // The number of variables in the model.
177 //
178 // Equal to the number of variables created minus the number of variables
179 // deleted.
180 inline int num_variables() const;
181
182 // The returned id of the next call to AddVariable.
183 //
184 // Equal to the number of variables created.
185 inline int next_variable_id() const;
186
187 // Returns true if this id has been created and not yet deleted.
188 inline bool has_variable(int id) const;
189
190 // Returns all the existing (created and not deleted) variables in the model
191 // in an arbitrary order.
192 std::vector<Variable> Variables();
193
194 // Returns all the existing (created and not deleted) variables in the model,
195 // sorted by id.
196 std::vector<Variable> SortedVariables();
197
198 std::vector<LinearConstraint> ColumnNonzeros(Variable variable);
199
200 // Adds a linear constraint to the model with bounds [-inf, +inf].
201 inline LinearConstraint AddLinearConstraint(absl::string_view name = "");
202
203 // Adds a linear constraint with bounds [lower_bound, upper_bound].
205 double upper_bound,
206 absl::string_view name = "");
207
208 // Adds a linear constraint from the given bounded linear expression.
209 //
210 // Usage:
211 // MathOpt model = ...;
212 // const Variable x = ...;
213 // const Variable y = ...;
214 // model.AddLinearConstraint(3 <= 2 * x + y + 1 <= 5, "c");
215 // // The new constraint formula is:
216 // // 3 - 1 <= 2 * x + y <= 5 - 1
217 // // Which is:
218 // // 2 <= 2 * x + y <= 4
219 // // since the offset has been removed from bounds.
220 //
221 // model.AddLinearConstraint(2 * x + y == x + 5 * z + 3);
222 // model.AddLinearConstraint(x >= 5);
224 const BoundedLinearExpression& bounded_expr, absl::string_view name = "");
225
226 // Removes a linear constraint from the model.
227 //
228 // It is an error to use any reference to this linear constraint after this
229 // operation. Runs in O(#variables in the linear constraint).
230 inline void DeleteLinearConstraint(LinearConstraint constraint);
231
232 // The number of linear constraints in the model.
233 //
234 // Equal to the number of linear constraints created minus the number of
235 // linear constraints deleted.
236 inline int num_linear_constraints() const;
237
238 // The returned id of the next call to AddLinearConstraint.
239 //
240 // Equal to the number of linear constraints created.
241 inline int next_linear_constraint_id() const;
242
243 // Returns true if this id has been created and not yet deleted.
244 inline bool has_linear_constraint(int id) const;
245
246 // Returns all the existing (created and not deleted) linear constraints in
247 // the model in an arbitrary order.
248 std::vector<LinearConstraint> LinearConstraints();
249
250 // Returns all the existing (created and not deleted) linear constraints in
251 // the model sorted by id.
252 std::vector<LinearConstraint> SortedLinearConstraints();
253
254 inline Objective objective();
255
256 // Solves the current optimization problem.
257 //
258 // A Status error will be returned if there is an unexpected failure in an
259 // underlying solver or for some internal MathOpt errors. Otherwise, check
260 // Result::termination_reason to see if an optimal solution was found.
261 //
262 // Memory model: the returned Result owns its own memory (for solutions, solve
263 // stats, etc.), EXPECT for a pointer back to this->model_. As a result:
264 // * Keep this alive to access Result
265 // * Avoid unnecessarily copying Result,
266 // * The result is generally accessible after mutating this, but some care
267 // is needed if Variables or LinearConstraints are added or deleted.
268 //
269 // Asserts (using CHECK) that the inputs model_parameters and
270 // callback_registration only contain variables and constraints from this
271 // model.
272 //
273 // See callback.h for documentation on callback and callback_registration.
274 absl::StatusOr<Result> Solve(
275 const SolveParametersProto& solver_parameters,
276 const ModelSolveParameters& model_parameters = {},
277 const CallbackRegistration& callback_registration = {},
278 Callback callback = nullptr);
279
280 ModelProto ExportModel() const;
281
282 // TODO(user): expose a way to efficiently iterate through the nonzeros of
283 // the linear constraint matrix.
284 private:
285 // Asserts (with CHECK) that the input pointer is either nullptr or that it
286 // points to the same model as model_.
287 void CheckModel(IndexedModel* model);
288 const SolverType solver_type_;
289 const SolverInitializerProto solver_initializer_;
290 const std::unique_ptr<IndexedModel> model_;
291 std::unique_ptr<Solver> solver_;
292 std::unique_ptr<IndexedModel::UpdateTracker> update_tracker_;
293};
294
296// Inline function implementations
298
299MathOpt::MathOpt(const SolverType solver_type, const absl::string_view name,
300 SolverInitializerProto solver_initializer)
301 : solver_type_(solver_type),
302 solver_initializer_(std::move(solver_initializer)),
303 model_(absl::make_unique<IndexedModel>(name)) {}
304
305const std::string& MathOpt::name() const { return model_->name(); }
306
307Variable MathOpt::AddVariable(const absl::string_view name) {
308 return Variable(model_.get(), model_->AddVariable(name));
309}
311 const double upper_bound, const bool is_integer,
312 const absl::string_view name) {
313 return Variable(model_.get(), model_->AddVariable(lower_bound, upper_bound,
314 is_integer, name));
315}
316
317Variable MathOpt::AddBinaryVariable(const absl::string_view name) {
318 return AddVariable(0.0, 1.0, true, name);
319}
320
322 const double upper_bound,
323 const absl::string_view name) {
324 return AddVariable(lower_bound, upper_bound, false, name);
325}
326
328 const double upper_bound,
329 const absl::string_view name) {
330 return AddVariable(lower_bound, upper_bound, true, name);
331}
332
333void MathOpt::DeleteVariable(const Variable variable) {
334 CHECK_EQ(model_.get(), variable.model());
335 model_->DeleteVariable(variable.typed_id());
336}
337
338int MathOpt::num_variables() const { return model_->num_variables(); }
339
341 return model_->next_variable_id().value();
342}
343
344bool MathOpt::has_variable(const int id) const {
345 return model_->has_variable(VariableId(id));
346}
347
349 return LinearConstraint(model_.get(), model_->AddLinearConstraint(name));
350}
352 const double upper_bound,
353 const absl::string_view name) {
354 return LinearConstraint(model_.get(), model_->AddLinearConstraint(
356}
357
359 CHECK_EQ(model_.get(), constraint.model());
360 model_->DeleteLinearConstraint(constraint.typed_id());
361}
362
364 return model_->num_linear_constraints();
365}
366
368 return model_->next_linear_constraint_id().value();
369}
370
371bool MathOpt::has_linear_constraint(const int id) const {
372 return model_->has_linear_constraint(LinearConstraintId(id));
373}
374
375Objective MathOpt::objective() { return Objective(model_.get()); }
376
377} // namespace math_opt
378} // namespace operations_research
379
380#endif // OR_TOOLS_MATH_OPT_CPP_MATH_OPT_H_
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:699
void DeleteVariable(Variable variable)
Definition: math_opt.h:333
std::function< CallbackResult(CallbackData)> Callback
Definition: math_opt.h:139
Variable AddBinaryVariable(absl::string_view name="")
Definition: math_opt.h:317
MathOpt & operator=(const MathOpt &)=delete
const std::string & name() const
Definition: math_opt.h:305
Variable AddIntegerVariable(double lower_bound, double upper_bound, absl::string_view name="")
Definition: math_opt.h:327
LinearConstraint AddLinearConstraint(absl::string_view name="")
Definition: math_opt.h:348
std::vector< Variable > Variables()
Definition: math_opt.cc:112
void DeleteLinearConstraint(LinearConstraint constraint)
Definition: math_opt.h:358
std::vector< LinearConstraint > ColumnNonzeros(Variable variable)
Definition: math_opt.cc:130
Variable AddContinuousVariable(double lower_bound, double upper_bound, absl::string_view name="")
Definition: math_opt.h:321
absl::StatusOr< Result > Solve(const SolveParametersProto &solver_parameters, const ModelSolveParameters &model_parameters={}, const CallbackRegistration &callback_registration={}, Callback callback=nullptr)
Definition: math_opt.cc:37
std::vector< LinearConstraint > LinearConstraints()
Definition: math_opt.cc:139
bool has_linear_constraint(int id) const
Definition: math_opt.h:371
std::vector< LinearConstraint > SortedLinearConstraints()
Definition: math_opt.cc:148
std::vector< Variable > SortedVariables()
Definition: math_opt.cc:121
Variable AddVariable(double lower_bound, double upper_bound, bool is_integer, absl::string_view name="")
Definition: math_opt.h:310
const std::string name
double upper_bound
double lower_bound
GRBmodel * model
MPCallback * callback
Definition: cleanup.h:22
Collection of objects used to extend the Constraint Solver library.
STL namespace.