Google OR-Tools v9.15
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-2025 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/base/nullability.h"
26#include "absl/log/check.h"
27#include "absl/log/die_if_null.h"
28#include "absl/status/status.h"
29#include "absl/status/statusor.h"
30#include "absl/strings/string_view.h"
31#include "absl/types/span.h"
50
51namespace operations_research {
52namespace math_opt {
53
54constexpr double kInf = std::numeric_limits<double>::infinity();
55
56absl::StatusOr<std::unique_ptr<Model>> Model::FromModelProto(
57 const ModelProto& model_proto) {
58 ASSIGN_OR_RETURN(absl_nonnull std::unique_ptr<ModelStorage> storage,
59 ModelStorage::FromModelProto(model_proto));
60 return std::make_unique<Model>(std::move(storage));
61}
62
63Model::Model(const absl::string_view name)
64 : storage_(std::make_shared<ModelStorage>(name)) {}
65
66Model::Model(absl_nonnull std::unique_ptr<ModelStorage> storage)
67 : storage_(ABSL_DIE_IF_NULL(std::move(storage))) {}
68
69absl_nonnull std::unique_ptr<Model> Model::Clone(
70 const std::optional<absl::string_view> new_name) const {
71 return std::make_unique<Model>(storage_->Clone(new_name));
72}
73
75 const BoundedLinearExpression& bounded_expr, absl::string_view name) {
76 CheckOptionalModel(bounded_expr.expression.storage());
77
78 const LinearConstraintId constraint = storage()->AddLinearConstraint(
79 bounded_expr.lower_bound_minus_offset(),
80 bounded_expr.upper_bound_minus_offset(), name);
81 for (const auto& [variable, coef] : bounded_expr.expression.terms()) {
82 storage()->set_linear_constraint_coefficient(constraint,
83 variable.typed_id(), coef);
84 }
85 return LinearConstraint(storage(), constraint);
86}
87
88std::vector<Variable> Model::Variables() const {
89 std::vector<Variable> result;
90 result.reserve(storage()->num_variables());
91 for (const VariableId var_id : storage()->variables()) {
92 result.push_back(Variable(storage(), var_id));
93 }
94 return result;
95}
96
97std::vector<Variable> Model::SortedVariables() const {
98 std::vector<Variable> result = Variables();
99 std::sort(result.begin(), result.end(),
100 [](const Variable& l, const Variable& r) {
101 return l.typed_id() < r.typed_id();
102 });
103 return result;
104}
105
106std::vector<LinearConstraint> Model::ColumnNonzeros(
107 const Variable variable) const {
108 CheckModel(variable.storage());
109 std::vector<LinearConstraint> result;
110 for (const LinearConstraintId constraint :
111 storage()->linear_constraints_with_variable(variable.typed_id())) {
112 result.push_back(LinearConstraint(storage(), constraint));
113 }
114 return result;
115}
116
117std::vector<Variable> Model::RowNonzeros(
118 const LinearConstraint constraint) const {
119 CheckModel(constraint.storage());
120 std::vector<Variable> result;
121 for (const VariableId variable :
122 storage()->variables_in_linear_constraint(constraint.typed_id())) {
123 result.push_back(Variable(storage(), variable));
124 }
125 return result;
126}
127
128std::vector<LinearConstraint> Model::LinearConstraints() const {
129 std::vector<LinearConstraint> result;
130 result.reserve(storage()->num_linear_constraints());
131 for (const LinearConstraintId lin_con_id : storage()->LinearConstraints()) {
132 result.push_back(LinearConstraint(storage(), lin_con_id));
133 }
134 return result;
135}
136
137std::vector<LinearConstraint> Model::SortedLinearConstraints() const {
138 std::vector<LinearConstraint> result = LinearConstraints();
139 std::sort(result.begin(), result.end(),
140 [](const LinearConstraint& l, const LinearConstraint& r) {
141 return l.typed_id() < r.typed_id();
142 });
143 return result;
144}
145
147 const bool is_maximize) {
148 CheckOptionalModel(objective.storage());
149 storage()->clear_objective(kPrimaryObjectiveId);
150 storage()->set_is_maximize(kPrimaryObjectiveId, is_maximize);
151 storage()->set_objective_offset(kPrimaryObjectiveId, objective.offset());
152 for (const auto& [var, coef] : objective.terms()) {
153 storage()->set_linear_objective_coefficient(kPrimaryObjectiveId,
154 var.typed_id(), coef);
155 }
156}
157
159 const bool is_maximize) {
160 CheckOptionalModel(objective.storage());
161 storage()->clear_objective(kPrimaryObjectiveId);
162 storage()->set_is_maximize(kPrimaryObjectiveId, is_maximize);
163 storage()->set_objective_offset(kPrimaryObjectiveId, objective.offset());
164 for (const auto& [var, coef] : objective.linear_terms()) {
165 storage()->set_linear_objective_coefficient(kPrimaryObjectiveId,
166 var.typed_id(), coef);
167 }
168 for (const auto& [vars, coef] : objective.quadratic_terms()) {
169 storage()->set_quadratic_objective_coefficient(
170 kPrimaryObjectiveId, vars.typed_id().first, vars.typed_id().second,
171 coef);
172 }
173}
174
175void Model::AddToObjective(const LinearExpression& objective_terms) {
176 CheckOptionalModel(objective_terms.storage());
177 storage()->set_objective_offset(
179 objective_terms.offset() +
181 for (const auto& [var, coef] : objective_terms.terms()) {
182 storage()->set_linear_objective_coefficient(
183 kPrimaryObjectiveId, var.typed_id(),
184 coef + storage()->linear_objective_coefficient(kPrimaryObjectiveId,
185 var.typed_id()));
186 }
187}
188
189void Model::AddToObjective(const QuadraticExpression& objective_terms) {
190 CheckOptionalModel(objective_terms.storage());
191 storage()->set_objective_offset(
193 objective_terms.offset() +
195 for (const auto& [var, coef] : objective_terms.linear_terms()) {
196 storage()->set_linear_objective_coefficient(
197 kPrimaryObjectiveId, var.typed_id(),
198 coef + storage()->linear_objective_coefficient(kPrimaryObjectiveId,
199 var.typed_id()));
200 }
201 for (const auto& [vars, coef] : objective_terms.quadratic_terms()) {
202 storage()->set_quadratic_objective_coefficient(
203 kPrimaryObjectiveId, vars.typed_id().first, vars.typed_id().second,
204 coef + storage()->quadratic_objective_coefficient(
205 kPrimaryObjectiveId, vars.typed_id().first,
206 vars.typed_id().second));
207 }
208}
209
211 CHECK_EQ(storage()->num_quadratic_objective_terms(kPrimaryObjectiveId), 0)
212 << "The objective function contains quadratic terms and cannot be "
213 "represented as a LinearExpression";
214 LinearExpression result = storage()->objective_offset(kPrimaryObjectiveId);
215 for (const auto& [v, coef] :
216 storage()->linear_objective(kPrimaryObjectiveId)) {
217 result += Variable(storage(), v) * coef;
218 }
219 return result;
220}
221
223 QuadraticExpression result = storage()->objective_offset(kPrimaryObjectiveId);
224 for (const auto& [v, coef] :
225 storage()->linear_objective(kPrimaryObjectiveId)) {
226 result += Variable(storage(), v) * coef;
227 }
228 for (const auto& [v1, v2, coef] :
229 storage()->quadratic_objective_terms(kPrimaryObjectiveId)) {
230 result +=
231 QuadraticTerm(Variable(storage(), v1), Variable(storage(), v2), coef);
232 }
233 return result;
234}
235
236std::vector<Objective> Model::AuxiliaryObjectives() const {
237 std::vector<Objective> result;
238 result.reserve(num_auxiliary_objectives());
239 for (const AuxiliaryObjectiveId id : storage()->AuxiliaryObjectives()) {
240 result.push_back(auxiliary_objective(id));
241 }
242 return result;
243}
244
245std::vector<Objective> Model::SortedAuxiliaryObjectives() const {
246 std::vector<Objective> result = AuxiliaryObjectives();
247 std::sort(result.begin(), result.end(),
248 [](const Objective& l, const Objective& r) {
249 return l.typed_id() < r.typed_id();
250 });
251 return result;
252}
253
254void Model::SetObjective(const Objective objective,
255 const LinearExpression& expression,
256 const bool is_maximize) {
257 CheckModel(objective.storage());
258 CheckOptionalModel(expression.storage());
259 storage()->clear_objective(objective.typed_id());
260 set_is_maximize(objective, is_maximize);
261 set_objective_offset(objective, expression.offset());
262 for (const auto [var, coef] : expression.terms()) {
263 set_objective_coefficient(objective, var, coef);
264 }
265}
266
267void Model::AddToObjective(const Objective objective,
268 const LinearExpression& expression) {
269 CheckModel(objective.storage());
270 CheckOptionalModel(expression.storage());
271 set_objective_offset(objective, objective.offset() + expression.offset());
272 for (const auto [var, coef] : expression.terms()) {
273 set_objective_coefficient(objective, var,
274 objective.coefficient(var) + coef);
275 }
276}
277
279 const Objective objective) const {
280 CheckModel(objective.storage());
281 std::vector<Variable> result;
282 result.reserve(storage()->num_linear_objective_terms(objective.typed_id()));
283 for (const auto [var_id, unused] :
284 storage()->linear_objective(objective.typed_id())) {
285 result.push_back(Variable(storage(), var_id));
286 }
287 return result;
288}
289
290std::vector<Variable> Model::NonzeroVariablesInQuadraticObjective() const {
291 std::vector<Variable> result;
292 for (const auto& [var_id1, var_id2, unused] :
293 storage()->quadratic_objective_terms(kPrimaryObjectiveId)) {
294 result.push_back(Variable(storage(), var_id1));
295 result.push_back(Variable(storage(), var_id2));
296 }
297 return result;
298}
299
300ModelProto Model::ExportModel(const bool remove_names) const {
301 return storage()->ExportModel(remove_names);
302}
303
304std::unique_ptr<UpdateTracker> Model::NewUpdateTracker() {
305 return std::make_unique<UpdateTracker>(storage_);
306}
307
308absl::Status Model::ApplyUpdateProto(const ModelUpdateProto& update_proto) {
309 return storage()->ApplyUpdateProto(update_proto);
310}
311
312std::ostream& operator<<(std::ostream& ostr, const Model& model) {
313 ostr << "Model";
314 if (!model.name().empty()) ostr << " " << model.name();
315 ostr << ":\n";
316
317 if (model.num_auxiliary_objectives() == 0) {
318 ostr << " Objective:\n"
319 << (model.is_maximize() ? " maximize " : " minimize ")
320 << model.ObjectiveAsQuadraticExpression() << "\n";
321 } else {
322 ostr << " Objectives:\n";
323 const auto stream_objective = [](std::ostream& ostr, const Objective obj) {
324 ostr << " " << obj << " (priority " << obj.priority()
325 << "): " << (obj.maximize() ? "maximize " : "minimize ")
326 << obj.AsQuadraticExpression() << "\n";
327 };
328 stream_objective(ostr, model.primary_objective());
329 for (const Objective obj : model.SortedAuxiliaryObjectives()) {
330 stream_objective(ostr, obj);
331 }
332 }
333
334 ostr << " Linear constraints:\n";
335 for (const LinearConstraint constraint : model.SortedLinearConstraints()) {
336 ostr << " " << constraint << ": " << constraint.AsBoundedLinearExpression()
337 << "\n";
338 }
339
340 if (model.num_quadratic_constraints() > 0) {
341 ostr << " Quadratic constraints:\n";
342 for (const QuadraticConstraint constraint :
344 ostr << " " << constraint << ": "
345 << constraint.AsBoundedQuadraticExpression() << "\n";
346 }
347 }
348
349 if (model.num_second_order_cone_constraints() > 0) {
350 ostr << " Second-order cone constraints:\n";
351 for (const SecondOrderConeConstraint constraint :
353 ostr << " " << constraint << ": " << constraint.ToString() << "\n";
354 }
355 }
356
357 if (model.num_sos1_constraints() > 0) {
358 ostr << " SOS1 constraints:\n";
359 for (const Sos1Constraint constraint : model.SortedSos1Constraints()) {
360 ostr << " " << constraint << ": " << constraint.ToString() << "\n";
361 }
362 }
363
364 if (model.num_sos2_constraints() > 0) {
365 ostr << " SOS2 constraints:\n";
366 for (const Sos2Constraint constraint : model.SortedSos2Constraints()) {
367 ostr << " " << constraint << ": " << constraint.ToString() << "\n";
368 }
369 }
370
371 if (model.num_indicator_constraints() > 0) {
372 ostr << " Indicator constraints:\n";
373 for (const IndicatorConstraint constraint :
375 ostr << " " << constraint << ": " << constraint.ToString() << "\n";
376 }
377 }
378
379 ostr << " Variables:\n";
380 for (const Variable v : model.SortedVariables()) {
381 ostr << " " << v;
382 if (v.is_integer()) {
383 if (v.lower_bound() == 0 && v.upper_bound() == 1) {
384 ostr << " (binary)\n";
385 continue;
386 }
387 ostr << " (integer)";
388 }
389 ostr << " in ";
390 if (v.lower_bound() == -kInf) {
391 ostr << "(-∞";
392 } else {
393 ostr << "[" << RoundTripDoubleFormat(v.lower_bound());
394 }
395 ostr << ", ";
396 if (v.upper_bound() == kInf) {
397 ostr << "+∞)";
398 } else {
399 ostr << RoundTripDoubleFormat(v.upper_bound()) << "]";
400 }
401 ostr << "\n";
402 }
403 return ostr;
404}
405
406// ------------------------- Quadratic constraints -----------------------------
407
409 const BoundedQuadraticExpression& bounded_expr,
410 const absl::string_view name) {
411 CheckOptionalModel(bounded_expr.expression.storage());
412 SparseCoefficientMap linear_terms;
413 for (const auto [var, coeff] : bounded_expr.expression.linear_terms()) {
414 linear_terms.set(var.typed_id(), coeff);
415 }
416 SparseSymmetricMatrix quadratic_terms;
417 for (const auto& [var_ids, coeff] :
418 bounded_expr.expression.quadratic_terms()) {
419 quadratic_terms.set(var_ids.typed_id().first, var_ids.typed_id().second,
420 coeff);
421 }
422 const QuadraticConstraintId id =
423 storage()->AddAtomicConstraint(QuadraticConstraintData{
424 .lower_bound = bounded_expr.lower_bound_minus_offset(),
425 .upper_bound = bounded_expr.upper_bound_minus_offset(),
426 .linear_terms = std::move(linear_terms),
427 .quadratic_terms = std::move(quadratic_terms),
428 .name = std::string(name),
429 });
430 return QuadraticConstraint(storage(), id);
431}
432
433// --------------------- Second-order cone constraints -------------------------
434
436 absl::Span<const LinearExpression> arguments_to_norm,
437 const LinearExpression& upper_bound, const absl::string_view name) {
438 CheckOptionalModel(upper_bound.storage());
439 std::vector<LinearExpressionData> arguments_to_norm_data;
440 arguments_to_norm_data.reserve(arguments_to_norm.size());
441 for (const LinearExpression& expr : arguments_to_norm) {
442 CheckOptionalModel(expr.storage());
443 arguments_to_norm_data.push_back(FromLinearExpression(expr));
444 }
445 const SecondOrderConeConstraintId id =
446 storage()->AddAtomicConstraint(SecondOrderConeConstraintData{
447 .upper_bound = FromLinearExpression(upper_bound),
448 .arguments_to_norm = std::move(arguments_to_norm_data),
449 .name = std::string(name),
450 });
452}
453
454// --------------------------- SOS1 constraints --------------------------------
455
456namespace {
457
458template <typename SosData>
459SosData MakeSosData(const std::vector<LinearExpression>& expressions,
460 std::vector<double> weights, const absl::string_view name) {
461 std::vector<LinearExpressionData> storage_expressions;
462 storage_expressions.reserve(expressions.size());
463 for (const LinearExpression& expr : expressions) {
464 LinearExpressionData& storage_expr = storage_expressions.emplace_back();
465 storage_expr.offset = expr.offset();
466 for (const auto& [var, coeff] : expr.terms()) {
467 storage_expr.coeffs.set(var.typed_id(), coeff);
468 }
469 }
470 return SosData(std::move(storage_expressions), std::move(weights),
471 std::string(name));
472}
473
474} // namespace
475
477 const std::vector<LinearExpression>& expressions,
478 std::vector<double> weights, const absl::string_view name) {
479 for (const LinearExpression& expr : expressions) {
480 CheckOptionalModel(expr.storage());
481 }
482 const Sos1ConstraintId id = storage()->AddAtomicConstraint(
483 MakeSosData<Sos1ConstraintData>(expressions, std::move(weights), name));
484 return Sos1Constraint(storage(), id);
485}
486
487// --------------------------- SOS2 constraints --------------------------------
488
490 const std::vector<LinearExpression>& expressions,
491 std::vector<double> weights, const absl::string_view name) {
492 for (const LinearExpression& expr : expressions) {
493 CheckOptionalModel(expr.storage());
494 }
495 const Sos2ConstraintId id = storage()->AddAtomicConstraint(
496 MakeSosData<Sos2ConstraintData>(expressions, std::move(weights), name));
497 return Sos2Constraint(storage(), id);
498}
499
500// --------------------------- Indicator constraints ---------------------------
501
503 const Variable indicator_variable,
504 const BoundedLinearExpression& implied_constraint,
505 const bool activate_on_zero, const absl::string_view name) {
506 CheckModel(indicator_variable.storage());
507 CheckOptionalModel(implied_constraint.expression.storage());
508 // We ignore the offset while unpacking here; instead, we account for it below
509 // by using the `{lower,upper}_bound_minus_offset` member functions.
510 auto [expr, _] = FromLinearExpression(implied_constraint.expression);
511 const IndicatorConstraintId id =
512 storage()->AddAtomicConstraint(IndicatorConstraintData{
513 .lower_bound = implied_constraint.lower_bound_minus_offset(),
514 .upper_bound = implied_constraint.upper_bound_minus_offset(),
515 .linear_terms = std::move(expr),
516 .indicator = indicator_variable.typed_id(),
517 .activate_on_zero = activate_on_zero,
518 .name = std::string(name),
519 });
520 return IndicatorConstraint(storage(), id);
521}
522
523} // namespace math_opt
524} // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
Sos1Constraint AddSos1Constraint(const std::vector< LinearExpression > &expressions, std::vector< double > weights={}, absl::string_view name="")
Definition model.cc:476
std::vector< IndicatorConstraint > SortedIndicatorConstraints() const
Definition model.h:1403
Objective auxiliary_objective(int64_t id) const
Definition model.h:1596
int64_t num_sos2_constraints() const
Definition model.h:1331
QuadraticConstraint AddQuadraticConstraint(const BoundedQuadraticExpression &bounded_expr, absl::string_view name="")
Definition model.cc:408
Sos2Constraint AddSos2Constraint(const std::vector< LinearExpression > &expressions, std::vector< double > weights={}, absl::string_view name="")
Definition model.cc:489
ModelProto ExportModel(bool remove_names=false) const
Definition model.cc:300
static absl::StatusOr< std::unique_ptr< Model > > FromModelProto(const ModelProto &model_proto)
Definition model.cc:56
std::vector< Sos2Constraint > SortedSos2Constraints() const
Definition model.h:1361
std::vector< Sos1Constraint > SortedSos1Constraints() const
Definition model.h:1320
absl::string_view name() const
Definition model.h:963
std::vector< QuadraticConstraint > SortedQuadraticConstraints() const
Definition model.h:1230
SecondOrderConeConstraint AddSecondOrderConeConstraint(absl::Span< const LinearExpression > arguments_to_norm, const LinearExpression &upper_bound, absl::string_view name="")
Definition model.cc:435
std::vector< Variable > NonzeroVariablesInLinearObjective() const
Definition model.h:1536
std::vector< SecondOrderConeConstraint > SortedSecondOrderConeConstraints() const
Definition model.h:1278
std::vector< Objective > AuxiliaryObjectives() const
Definition model.cc:236
int64_t num_second_order_cone_constraints() const
Definition model.h:1242
std::unique_ptr< UpdateTracker > NewUpdateTracker()
Definition model.cc:304
std::vector< LinearConstraint > SortedLinearConstraints() const
Definition model.cc:137
void set_is_maximize(bool is_maximize)
Definition model.h:1532
int64_t num_sos1_constraints() const
Definition model.h:1290
Variable variable(int64_t id) const
Definition model.h:1010
QuadraticExpression ObjectiveAsQuadraticExpression() const
Definition model.cc:222
void AddToObjective(double objective)
Definition model.h:1451
ModelStorageCPtr storage() const
Definition model.h:921
void set_objective_offset(double value)
Definition model.h:1520
absl::Status ApplyUpdateProto(const ModelUpdateProto &update_proto)
Definition model.cc:308
std::vector< LinearConstraint > ColumnNonzeros(Variable variable) const
Definition model.cc:106
int64_t num_indicator_constraints() const
Definition model.h:1372
std::vector< LinearConstraint > LinearConstraints() const
Definition model.cc:128
std::vector< Objective > SortedAuxiliaryObjectives() const
Definition model.cc:245
absl_nonnull std::unique_ptr< Model > Clone(std::optional< absl::string_view > new_name=std::nullopt) const
Definition model.cc:69
int64_t num_auxiliary_objectives() const
Definition model.h:1580
void set_objective_coefficient(Variable variable, double value)
Definition model.h:1480
double upper_bound(Variable variable) const
Definition model.h:1039
std::vector< Variable > Variables() const
Definition model.cc:88
std::vector< Variable > NonzeroVariablesInQuadraticObjective() const
Definition model.cc:290
std::vector< Variable > SortedVariables() const
Definition model.cc:97
Model(absl::string_view name="")
Definition model.cc:63
Objective primary_objective() const
Definition model.h:1461
LinearExpression ObjectiveAsLinearExpression() const
Definition model.cc:210
int64_t num_quadratic_constraints() const
Definition model.h:1198
void SetObjective(double objective, bool is_maximize)
Definition model.h:1441
IndicatorConstraint AddIndicatorConstraint(Variable indicator_variable, const BoundedLinearExpression &implied_constraint, bool activate_on_zero=false, absl::string_view name={})
Definition model.cc:502
LinearConstraint AddLinearConstraint(absl::string_view name="")
Definition model.h:1080
std::vector< Variable > RowNonzeros(LinearConstraint constraint) const
Definition model.cc:117
double coefficient(Variable variable) const
Definition objective.h:166
const QuadraticTermMap< double > & quadratic_terms() const
bool set(VariableId first, VariableId second, double value)
ElementId< ElementType::kAuxiliaryObjective > AuxiliaryObjectiveId
Definition elements.h:82
ElementId< ElementType::kVariable > VariableId
Definition elements.h:80
constexpr ObjectiveId kPrimaryObjectiveId
ElementId< ElementType::kQuadraticConstraint > QuadraticConstraintId
Definition elements.h:83
std::ostream & operator<<(std::ostream &ostr, const SecondOrderConeConstraint &constraint)
ElementId< ElementType::kLinearConstraint > LinearConstraintId
Definition elements.h:81
LinearExpressionData FromLinearExpression(const LinearExpression &expression)
Definition model_util.cc:34
ElementId< ElementType::kIndicatorConstraint > IndicatorConstraintId
Definition elements.h:84
OR-Tools root namespace.
STL namespace.