Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
model.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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
15
16#include <algorithm>
17#include <limits>
18#include <memory>
19#include <optional>
20#include <ostream>
21#include <string>
22#include <utility>
23#include <vector>
24
25#include "absl/log/check.h"
26#include "absl/status/status.h"
27#include "absl/status/statusor.h"
28#include "absl/strings/string_view.h"
29#include "absl/types/span.h"
47
48namespace operations_research {
49namespace math_opt {
50
51constexpr double kInf = std::numeric_limits<double>::infinity();
52
53absl::StatusOr<std::unique_ptr<Model>> Model::FromModelProto(
54 const ModelProto& model_proto) {
55 ASSIGN_OR_RETURN(std::unique_ptr<ModelStorage> storage,
56 ModelStorage::FromModelProto(model_proto));
57 return std::make_unique<Model>(std::move(storage));
58}
59
60Model::Model(const absl::string_view name)
61 : storage_(std::make_shared<ModelStorage>(name)) {}
62
63Model::Model(std::unique_ptr<ModelStorage> storage)
64 : storage_(std::move(storage)) {}
65
66std::unique_ptr<Model> Model::Clone(
67 const std::optional<absl::string_view> new_name) const {
68 return std::make_unique<Model>(storage_->Clone(new_name));
69}
70
72 const BoundedLinearExpression& bounded_expr, absl::string_view name) {
73 CheckOptionalModel(bounded_expr.expression.storage());
74
75 const LinearConstraintId constraint = storage()->AddLinearConstraint(
76 bounded_expr.lower_bound_minus_offset(),
77 bounded_expr.upper_bound_minus_offset(), name);
78 for (const auto& [variable, coef] : bounded_expr.expression.terms()) {
79 storage()->set_linear_constraint_coefficient(constraint,
81 }
82 return LinearConstraint(storage(), constraint);
83}
84
85std::vector<Variable> Model::Variables() const {
86 std::vector<Variable> result;
87 result.reserve(storage()->num_variables());
88 for (const VariableId var_id : storage()->variables()) {
89 result.push_back(Variable(storage(), var_id));
90 }
91 return result;
92}
93
94std::vector<Variable> Model::SortedVariables() const {
95 std::vector<Variable> result = Variables();
96 std::sort(result.begin(), result.end(),
97 [](const Variable& l, const Variable& r) {
98 return l.typed_id() < r.typed_id();
99 });
100 return result;
101}
102
103std::vector<LinearConstraint> Model::ColumnNonzeros(
104 const Variable variable) const {
105 CheckModel(variable.storage());
106 std::vector<LinearConstraint> result;
107 for (const LinearConstraintId constraint :
108 storage()->linear_constraints_with_variable(variable.typed_id())) {
109 result.push_back(LinearConstraint(storage(), constraint));
110 }
111 return result;
112}
113
114std::vector<Variable> Model::RowNonzeros(
115 const LinearConstraint constraint) const {
116 CheckModel(constraint.storage());
117 std::vector<Variable> result;
118 for (const VariableId variable :
119 storage()->variables_in_linear_constraint(constraint.typed_id())) {
120 result.push_back(Variable(storage(), variable));
121 }
122 return result;
123}
124
125std::vector<LinearConstraint> Model::LinearConstraints() const {
126 std::vector<LinearConstraint> result;
127 result.reserve(storage()->num_linear_constraints());
128 for (const LinearConstraintId lin_con_id : storage()->LinearConstraints()) {
129 result.push_back(LinearConstraint(storage(), lin_con_id));
130 }
131 return result;
132}
133
134std::vector<LinearConstraint> Model::SortedLinearConstraints() const {
135 std::vector<LinearConstraint> result = LinearConstraints();
136 std::sort(result.begin(), result.end(),
137 [](const LinearConstraint& l, const LinearConstraint& r) {
138 return l.typed_id() < r.typed_id();
139 });
140 return result;
141}
142
144 const bool is_maximize) {
145 CheckOptionalModel(objective.storage());
146 storage()->clear_objective(kPrimaryObjectiveId);
147 storage()->set_is_maximize(kPrimaryObjectiveId, is_maximize);
148 storage()->set_objective_offset(kPrimaryObjectiveId, objective.offset());
149 for (const auto& [var, coef] : objective.terms()) {
150 storage()->set_linear_objective_coefficient(kPrimaryObjectiveId,
151 var.typed_id(), coef);
152 }
153}
154
156 const bool is_maximize) {
157 CheckOptionalModel(objective.storage());
158 storage()->clear_objective(kPrimaryObjectiveId);
159 storage()->set_is_maximize(kPrimaryObjectiveId, is_maximize);
160 storage()->set_objective_offset(kPrimaryObjectiveId, objective.offset());
161 for (const auto& [var, coef] : objective.linear_terms()) {
162 storage()->set_linear_objective_coefficient(kPrimaryObjectiveId,
163 var.typed_id(), coef);
164 }
165 for (const auto& [vars, coef] : objective.quadratic_terms()) {
166 storage()->set_quadratic_objective_coefficient(
167 kPrimaryObjectiveId, vars.typed_id().first, vars.typed_id().second,
168 coef);
169 }
170}
171
172void Model::AddToObjective(const LinearExpression& objective_terms) {
173 CheckOptionalModel(objective_terms.storage());
174 storage()->set_objective_offset(
176 objective_terms.offset() +
178 for (const auto& [var, coef] : objective_terms.terms()) {
179 storage()->set_linear_objective_coefficient(
180 kPrimaryObjectiveId, var.typed_id(),
181 coef + storage()->linear_objective_coefficient(kPrimaryObjectiveId,
182 var.typed_id()));
183 }
184}
185
186void Model::AddToObjective(const QuadraticExpression& objective_terms) {
187 CheckOptionalModel(objective_terms.storage());
188 storage()->set_objective_offset(
190 objective_terms.offset() +
192 for (const auto& [var, coef] : objective_terms.linear_terms()) {
193 storage()->set_linear_objective_coefficient(
194 kPrimaryObjectiveId, var.typed_id(),
195 coef + storage()->linear_objective_coefficient(kPrimaryObjectiveId,
196 var.typed_id()));
197 }
198 for (const auto& [vars, coef] : objective_terms.quadratic_terms()) {
199 storage()->set_quadratic_objective_coefficient(
200 kPrimaryObjectiveId, vars.typed_id().first, vars.typed_id().second,
201 coef + storage()->quadratic_objective_coefficient(
202 kPrimaryObjectiveId, vars.typed_id().first,
203 vars.typed_id().second));
204 }
205}
206
208 CHECK_EQ(storage()->num_quadratic_objective_terms(kPrimaryObjectiveId), 0)
209 << "The objective function contains quadratic terms and cannot be "
210 "represented as a LinearExpression";
211 LinearExpression result = storage()->objective_offset(kPrimaryObjectiveId);
212 for (const auto& [v, coef] :
213 storage()->linear_objective(kPrimaryObjectiveId)) {
214 result += Variable(storage(), v) * coef;
215 }
216 return result;
217}
218
220 QuadraticExpression result = storage()->objective_offset(kPrimaryObjectiveId);
221 for (const auto& [v, coef] :
222 storage()->linear_objective(kPrimaryObjectiveId)) {
223 result += Variable(storage(), v) * coef;
224 }
225 for (const auto& [v1, v2, coef] :
226 storage()->quadratic_objective_terms(kPrimaryObjectiveId)) {
227 result +=
229 }
230 return result;
231}
232
233std::vector<Objective> Model::AuxiliaryObjectives() const {
234 std::vector<Objective> result;
235 result.reserve(num_auxiliary_objectives());
236 for (const AuxiliaryObjectiveId id : storage()->AuxiliaryObjectives()) {
237 result.push_back(auxiliary_objective(id));
238 }
239 return result;
240}
241
242std::vector<Objective> Model::SortedAuxiliaryObjectives() const {
243 std::vector<Objective> result = AuxiliaryObjectives();
244 std::sort(result.begin(), result.end(),
245 [](const Objective& l, const Objective& r) {
246 return l.typed_id() < r.typed_id();
247 });
248 return result;
249}
250
251void Model::SetObjective(const Objective objective,
252 const LinearExpression& expression,
253 const bool is_maximize) {
254 CheckModel(objective.storage());
255 CheckOptionalModel(expression.storage());
256 storage()->clear_objective(objective.typed_id());
257 set_is_maximize(objective, is_maximize);
258 set_objective_offset(objective, expression.offset());
259 for (const auto [var, coef] : expression.terms()) {
261 }
262}
263
265 const LinearExpression& expression) {
266 CheckModel(objective.storage());
267 CheckOptionalModel(expression.storage());
268 set_objective_offset(objective, objective.offset() + expression.offset());
269 for (const auto [var, coef] : expression.terms()) {
271 objective.coefficient(var) + coef);
272 }
273}
274
275ModelProto Model::ExportModel(const bool remove_names) const {
276 return storage()->ExportModel(remove_names);
277}
278
279std::unique_ptr<UpdateTracker> Model::NewUpdateTracker() {
280 return std::make_unique<UpdateTracker>(storage_);
281}
282
283absl::Status Model::ApplyUpdateProto(const ModelUpdateProto& update_proto) {
284 return storage()->ApplyUpdateProto(update_proto);
285}
286
287std::ostream& operator<<(std::ostream& ostr, const Model& model) {
288 ostr << "Model";
289 if (!model.name().empty()) ostr << " " << model.name();
290 ostr << ":\n";
291
292 if (model.num_auxiliary_objectives() == 0) {
293 ostr << " Objective:\n"
294 << (model.is_maximize() ? " maximize " : " minimize ")
295 << model.ObjectiveAsQuadraticExpression() << "\n";
296 } else {
297 ostr << " Objectives:\n";
298 const auto stream_objective = [](std::ostream& ostr, const Objective obj) {
299 ostr << " " << obj << " (priority " << obj.priority()
300 << "): " << (obj.maximize() ? "maximize " : "minimize ")
301 << obj.AsQuadraticExpression() << "\n";
302 };
303 stream_objective(ostr, model.primary_objective());
304 for (const Objective obj : model.SortedAuxiliaryObjectives()) {
305 stream_objective(ostr, obj);
306 }
307 }
308
309 ostr << " Linear constraints:\n";
310 for (const LinearConstraint constraint : model.SortedLinearConstraints()) {
311 ostr << " " << constraint << ": " << constraint.AsBoundedLinearExpression()
312 << "\n";
313 }
314
315 if (model.num_quadratic_constraints() > 0) {
316 ostr << " Quadratic constraints:\n";
317 for (const QuadraticConstraint constraint :
318 model.SortedQuadraticConstraints()) {
319 ostr << " " << constraint << ": "
320 << constraint.AsBoundedQuadraticExpression() << "\n";
321 }
322 }
323
324 if (model.num_second_order_cone_constraints() > 0) {
325 ostr << " Second-order cone constraints:\n";
326 for (const SecondOrderConeConstraint constraint :
327 model.SortedSecondOrderConeConstraints()) {
328 ostr << " " << constraint << ": " << constraint.ToString() << "\n";
329 }
330 }
331
332 if (model.num_sos1_constraints() > 0) {
333 ostr << " SOS1 constraints:\n";
334 for (const Sos1Constraint constraint : model.SortedSos1Constraints()) {
335 ostr << " " << constraint << ": " << constraint.ToString() << "\n";
336 }
337 }
338
339 if (model.num_sos2_constraints() > 0) {
340 ostr << " SOS2 constraints:\n";
341 for (const Sos2Constraint constraint : model.SortedSos2Constraints()) {
342 ostr << " " << constraint << ": " << constraint.ToString() << "\n";
343 }
344 }
345
346 if (model.num_indicator_constraints() > 0) {
347 ostr << " Indicator constraints:\n";
348 for (const IndicatorConstraint constraint :
349 model.SortedIndicatorConstraints()) {
350 ostr << " " << constraint << ": " << constraint.ToString() << "\n";
351 }
352 }
353
354 ostr << " Variables:\n";
355 for (const Variable v : model.SortedVariables()) {
356 ostr << " " << v;
357 if (v.is_integer()) {
358 if (v.lower_bound() == 0 && v.upper_bound() == 1) {
359 ostr << " (binary)\n";
360 continue;
361 }
362 ostr << " (integer)";
363 }
364 ostr << " in ";
365 if (v.lower_bound() == -kInf) {
366 ostr << "(-∞";
367 } else {
368 ostr << "[" << RoundTripDoubleFormat(v.lower_bound());
369 }
370 ostr << ", ";
371 if (v.upper_bound() == kInf) {
372 ostr << "+∞)";
373 } else {
374 ostr << RoundTripDoubleFormat(v.upper_bound()) << "]";
375 }
376 ostr << "\n";
377 }
378 return ostr;
379}
380
381// ------------------------- Quadratic constraints -----------------------------
382
384 const BoundedQuadraticExpression& bounded_expr,
385 const absl::string_view name) {
386 CheckOptionalModel(bounded_expr.expression.storage());
387 SparseCoefficientMap linear_terms;
388 for (const auto [var, coeff] : bounded_expr.expression.linear_terms()) {
389 linear_terms.set(var.typed_id(), coeff);
390 }
391 SparseSymmetricMatrix quadratic_terms;
392 for (const auto& [var_ids, coeff] :
393 bounded_expr.expression.quadratic_terms()) {
394 quadratic_terms.set(var_ids.typed_id().first, var_ids.typed_id().second,
395 coeff);
396 }
397 const QuadraticConstraintId id =
398 storage()->AddAtomicConstraint(QuadraticConstraintData{
399 .lower_bound = bounded_expr.lower_bound_minus_offset(),
400 .upper_bound = bounded_expr.upper_bound_minus_offset(),
401 .linear_terms = std::move(linear_terms),
402 .quadratic_terms = std::move(quadratic_terms),
403 .name = std::string(name),
404 });
405 return QuadraticConstraint(storage(), id);
406}
407
408// --------------------- Second-order cone constraints -------------------------
409
411 absl::Span<const LinearExpression> arguments_to_norm,
412 const LinearExpression& upper_bound, const absl::string_view name) {
413 CheckOptionalModel(upper_bound.storage());
414 std::vector<LinearExpressionData> arguments_to_norm_data;
415 arguments_to_norm_data.reserve(arguments_to_norm.size());
416 for (const LinearExpression& expr : arguments_to_norm) {
417 CheckOptionalModel(expr.storage());
418 arguments_to_norm_data.push_back(FromLinearExpression(expr));
419 }
420 const SecondOrderConeConstraintId id =
421 storage()->AddAtomicConstraint(SecondOrderConeConstraintData{
422 .upper_bound = FromLinearExpression(upper_bound),
423 .arguments_to_norm = std::move(arguments_to_norm_data),
424 .name = std::string(name),
425 });
427}
428
429// --------------------------- SOS1 constraints --------------------------------
430
431namespace {
432
433template <typename SosData>
434SosData MakeSosData(const std::vector<LinearExpression>& expressions,
435 std::vector<double> weights, const absl::string_view name) {
436 std::vector<LinearExpressionData> storage_expressions;
437 storage_expressions.reserve(expressions.size());
438 for (const LinearExpression& expr : expressions) {
439 LinearExpressionData& storage_expr = storage_expressions.emplace_back();
440 storage_expr.offset = expr.offset();
441 for (const auto& [var, coeff] : expr.terms()) {
442 storage_expr.coeffs.set(var.typed_id(), coeff);
443 }
444 }
445 return SosData(std::move(storage_expressions), std::move(weights),
446 std::string(name));
447}
448
449} // namespace
450
452 const std::vector<LinearExpression>& expressions,
453 std::vector<double> weights, const absl::string_view name) {
454 for (const LinearExpression& expr : expressions) {
455 CheckOptionalModel(expr.storage());
456 }
457 const Sos1ConstraintId id = storage()->AddAtomicConstraint(
458 MakeSosData<Sos1ConstraintData>(expressions, std::move(weights), name));
459 return Sos1Constraint(storage(), id);
460}
461
462// --------------------------- SOS2 constraints --------------------------------
463
465 const std::vector<LinearExpression>& expressions,
466 std::vector<double> weights, const absl::string_view name) {
467 for (const LinearExpression& expr : expressions) {
468 CheckOptionalModel(expr.storage());
469 }
470 const Sos2ConstraintId id = storage()->AddAtomicConstraint(
471 MakeSosData<Sos2ConstraintData>(expressions, std::move(weights), name));
472 return Sos2Constraint(storage(), id);
473}
474
475// --------------------------- Indicator constraints ---------------------------
476
478 const Variable indicator_variable,
479 const BoundedLinearExpression& implied_constraint,
480 const bool activate_on_zero, const absl::string_view name) {
481 CheckModel(indicator_variable.storage());
482 CheckOptionalModel(implied_constraint.expression.storage());
483 // We ignore the offset while unpacking here; instead, we account for it below
484 // by using the `{lower,upper}_bound_minus_offset` member functions.
485 auto [expr, _] = FromLinearExpression(implied_constraint.expression);
486 const IndicatorConstraintId id =
487 storage()->AddAtomicConstraint(IndicatorConstraintData{
488 .lower_bound = implied_constraint.lower_bound_minus_offset(),
489 .upper_bound = implied_constraint.upper_bound_minus_offset(),
490 .linear_terms = std::move(expr),
491 .indicator = indicator_variable.typed_id(),
492 .activate_on_zero = activate_on_zero,
493 .name = std::string(name),
494 });
495 return IndicatorConstraint(storage(), id);
496}
497
498} // namespace math_opt
499} // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
const VariableMap< double > & terms() const
Returns the terms in this expression.
Sos1Constraint AddSos1Constraint(const std::vector< LinearExpression > &expressions, std::vector< double > weights={}, absl::string_view name="")
Definition model.cc:451
Objective auxiliary_objective(int64_t id) const
Will CHECK if has_auxiliary_objective(id) is false.
Definition model.h:1572
const ModelStorage * storage() const
Definition model.h:905
QuadraticConstraint AddQuadraticConstraint(const BoundedQuadraticExpression &bounded_expr, absl::string_view name="")
----------------------— Quadratic constraints --------------------------—
Definition model.cc:383
Sos2Constraint AddSos2Constraint(const std::vector< LinearExpression > &expressions, std::vector< double > weights={}, absl::string_view name="")
------------------------— SOS2 constraints -----------------------------—
Definition model.cc:464
ModelProto ExportModel(bool remove_names=false) const
Definition model.cc:275
static absl::StatusOr< std::unique_ptr< Model > > FromModelProto(const ModelProto &model_proto)
Definition model.cc:53
SecondOrderConeConstraint AddSecondOrderConeConstraint(absl::Span< const LinearExpression > arguments_to_norm, const LinearExpression &upper_bound, absl::string_view name="")
------------------— Second-order cone constraints ----------------------—
Definition model.cc:410
std::vector< Objective > AuxiliaryObjectives() const
Definition model.cc:233
std::unique_ptr< UpdateTracker > NewUpdateTracker()
Definition model.cc:279
std::vector< LinearConstraint > SortedLinearConstraints() const
Definition model.cc:134
void set_is_maximize(bool is_maximize)
Prefer set_maximize() and set_minimize() above for more readable code.
Definition model.h:1512
std::unique_ptr< Model > Clone(std::optional< absl::string_view > new_name=std::nullopt) const
Definition model.cc:66
Variable variable(int64_t id) const
Will CHECK if has_variable(id) is false.
Definition model.h:994
QuadraticExpression ObjectiveAsQuadraticExpression() const
Definition model.cc:219
void AddToObjective(double objective)
Adds the provided expression terms to the objective.
Definition model.h:1431
void set_objective_offset(double value)
Definition model.h:1500
absl::Status ApplyUpdateProto(const ModelUpdateProto &update_proto)
Definition model.cc:283
std::vector< LinearConstraint > ColumnNonzeros(Variable variable) const
Definition model.cc:103
std::vector< LinearConstraint > LinearConstraints() const
Definition model.cc:125
std::vector< Objective > SortedAuxiliaryObjectives() const
Definition model.cc:242
int64_t num_auxiliary_objectives() const
Definition model.h:1556
void set_objective_coefficient(Variable variable, double value)
Definition model.h:1460
std::vector< Variable > Variables() const
Definition model.cc:85
std::vector< Variable > SortedVariables() const
Definition model.cc:94
Model(absl::string_view name="")
Creates an empty minimization problem.
Definition model.cc:60
LinearExpression ObjectiveAsLinearExpression() const
Definition model.cc:207
void SetObjective(double objective, bool is_maximize)
Sets the objective to optimize the provided expression.
Definition model.h:1421
IndicatorConstraint AddIndicatorConstraint(Variable indicator_variable, const BoundedLinearExpression &implied_constraint, bool activate_on_zero=false, absl::string_view name={})
------------------------— Indicator constraints ------------------------—
Definition model.cc:477
const std::string & name() const
----------------------------— Variables --------------------------------—
Definition model.h:947
LinearConstraint AddLinearConstraint(absl::string_view name="")
Adds a linear constraint to the model with bounds [-inf, +inf].
Definition model.h:1064
std::vector< Variable > RowNonzeros(LinearConstraint constraint) const
Definition model.cc:114
double coefficient(Variable variable) const
Returns the linear coefficient for the variable in the model.
Definition objective.h:171
const ModelStorage * storage() const
Returns a const-pointer to the underlying storage object for the model.
Definition objective.h:144
double offset() const
Returns the constant offset of the objective.
Definition objective.h:161
const QuadraticTermMap< double > & quadratic_terms() const
bool set(VariableId id, double coeff)
Returns true if the stored value changes.
bool set(VariableId first, VariableId second, double value)
const std::string name
A name for logging purposes.
IntVar * var
int64_t coef
double upper_bound
GRBmodel * model
std::ostream & operator<<(std::ostream &ostr, const IndicatorConstraint &constraint)
constexpr ObjectiveId kPrimaryObjectiveId
LinearExpressionData FromLinearExpression(const LinearExpression &expression)
Converts a LinearExpression to the associated "raw ID" format.
Definition model_util.cc:34
In SWIG mode, we don't want anything besides these top-level includes.
STL namespace.
A LinearExpression with upper and lower bounds.
A QuadraticExpression with upper and lower bounds.