Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
model_storage_v2.h
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
14#ifndef OR_TOOLS_MATH_OPT_STORAGE_MODEL_STORAGE_V2_H_
15#define OR_TOOLS_MATH_OPT_STORAGE_MODEL_STORAGE_V2_H_
16
17#include <cstdint>
18#include <memory>
19#include <optional>
20#include <string>
21#include <tuple>
22#include <utility>
23#include <vector>
24
25#include "absl/algorithm/container.h"
26#include "absl/base/nullability.h"
27#include "absl/container/flat_hash_map.h"
28#include "absl/log/check.h"
29#include "absl/log/log.h"
30#include "absl/status/status.h"
31#include "absl/status/statusor.h"
32#include "absl/strings/string_view.h"
33#include "ortools/math_opt/constraints/indicator/storage.h" // IWYU pragma: export
34#include "ortools/math_opt/constraints/quadratic/storage.h" // IWYU pragma: export
35#include "ortools/math_opt/constraints/sos/storage.h" // IWYU pragma: export
41#include "ortools/math_opt/model.pb.h"
42#include "ortools/math_opt/model_update.pb.h"
43#include "ortools/math_opt/sparse_containers.pb.h"
47
49
50// An index based C++ API for building & storing optimization problems.
51//
52// Note that this API should usually not be used by C++ users that should prefer
53// the math_opt/cpp/model.h API.
54//
55// It supports the efficient creation and modification of an optimization model,
56// and the export of ModelProto and ModelUpdateProto protos.
57//
58// All methods run in amortized O(1) (as amortized over calls to that exact
59// function) unless otherwise specified.
60//
61// Incrementalism, the ModelUpdate proto, and Checkpoints:
62//
63// To update an existing model as specified by a Model proto, solvers consume a
64// ModelUpdate proto, which describes the changes to a model (e.g. new variables
65// or a change in a variable bound). ModelStorage::NewUpdateTracker() tracks the
66// changes made and produces a ModelUpdate proto describing these changes with
67// the method ModelStorage::ExportModelUpdate(). The changes returned will be
68// the modifications since the previous call to
69// ModelStorage::AdvanceCheckpoint(). Note that, for newly initialized models,
70// before the first checkpoint, there is no additional memory overhead from
71// tracking changes. See
72// g3doc/ortools/math_opt/g3doc/model_building_complexity.md
73// for details.
74//
75// On bad input:
76//
77// Using a bad variable id or constraint id (an id not in the current model,
78// which includes ids that have been deleted) on any method will result in an
79// immediate failure (either a CHECK failure or an exception, which is an
80// implementation detail you should not rely on). We make no attempt to say if a
81// model is invalid (e.g. a variable lower bound is infinite, exceeds an upper
82// bound, or is NaN). The exported models are validated instead, see
83// model_validator.h.
85 public:
86 // Returns a storage from the input proto. Returns a failure status if the
87 // input proto is invalid.
88 //
89 // Variable/constraint names can be repeated in the input proto but will be
90 // considered invalid when solving.
91 //
92 // See ApplyUpdateProto() for dealing with subsequent updates.
93 static absl::StatusOr<absl::Nonnull<std::unique_ptr<ModelStorageV2>>>
94 FromModelProto(const ModelProto& model_proto);
95
96 // Creates an empty minimization problem.
97 inline explicit ModelStorageV2(absl::string_view model_name = "",
98 absl::string_view primary_objective_name = "");
99
102
103 // Returns a clone of the model, optionally changing model's name.
104 //
105 // The variables and constraints have the same ids. The clone will also not
106 // reused any id of variable/constraint that was deleted in the original.
107 //
108 // Note that the returned model does not have any update tracker.
109 absl::Nonnull<std::unique_ptr<ModelStorageV2>> Clone(
110 std::optional<absl::string_view> new_name = std::nullopt) const;
111
112 inline const std::string& name() const { return elemental_.model_name(); }
113
115 // Variables
117
118 // Adds a continuous unbounded variable to the model and returns its id.
119 //
120 // See AddVariable(double, double, bool, absl::string_view) for details.
121 inline VariableId AddVariable(absl::string_view name = "");
122
123 // Adds a variable to the model and returns its id.
124 //
125 // The returned ids begin at zero and increase by one with each call to
126 // AddVariable. Deleted ids are NOT reused. If no variables are deleted,
127 // the ids in the model will be consecutive.
128 inline VariableId AddVariable(double lower_bound, double upper_bound,
129 bool is_integer, absl::string_view name = "");
130
131 inline double variable_lower_bound(VariableId id) const;
132 inline double variable_upper_bound(VariableId id) const;
133 inline bool is_variable_integer(VariableId id) const;
134 inline absl::string_view variable_name(VariableId id) const;
135
136 inline void set_variable_lower_bound(VariableId id, double lower_bound);
137 inline void set_variable_upper_bound(VariableId id, double upper_bound);
138 inline void set_variable_is_integer(VariableId id, bool is_integer);
139 inline void set_variable_as_integer(VariableId id);
141
142 // Removes a variable from the model.
143 //
144 // It is an error to use a deleted variable id as input to any subsequent
145 // function calls on the model. Runs in O(#constraints containing the
146 // variable).
147 void DeleteVariable(VariableId id);
148
149 // The number of variables in the model.
150 //
151 // Equal to the number of variables created minus the number of variables
152 // deleted.
153 inline int64_t num_variables() const;
154
155 // The returned id of the next call to AddVariable.
156 //
157 // Equal to the number of variables created.
158 inline VariableId next_variable_id() const;
159
160 // Sets the next variable id to be the maximum of next_variable_id() and id.
162
163 // Returns true if this id has been created and not yet deleted.
164 inline bool has_variable(VariableId id) const;
165
166 ABSL_DEPRECATED("Use Variables() instead")
167 std::vector<VariableId> variables() const { return Variables(); }
168
169 // The VariableIds in use (not deleted), order not defined.
170 std::vector<VariableId> Variables() const;
171
172 // Returns a sorted vector of all existing (not deleted) variables in the
173 // model.
174 //
175 // Runs in O(n log(n)), where n is the number of variables returned.
176 std::vector<VariableId> SortedVariables() const;
177
179 // Linear Constraints
181
182 // Adds a linear constraint to the model with a lower bound of -inf and an
183 // upper bound of +inf and returns its id.
184 //
185 // See AddLinearConstraint(double, double, absl::string_view) for details.
186 inline LinearConstraintId AddLinearConstraint(absl::string_view name = "");
187
188 // Adds a linear constraint to the model returns its id.
189 //
190 // The returned ids begin at zero and increase by one with each call to
191 // AddLinearConstraint. Deleted ids are NOT reused. If no linear
192 // constraints are deleted, the ids in the model will be consecutive.
193 inline LinearConstraintId AddLinearConstraint(double lower_bound,
194 double upper_bound,
195 absl::string_view name = "");
196
199 inline absl::string_view linear_constraint_name(LinearConstraintId id) const;
200
202 double lower_bound);
204 double upper_bound);
205
206 // Removes a linear constraint from the model.
207 //
208 // It is an error to use a deleted linear constraint id as input to any
209 // subsequent function calls on the model. Runs in O(#variables in the linear
210 // constraint).
212
213 // The number of linear constraints in the model.
214 //
215 // Equal to the number of linear constraints created minus the number of
216 // linear constraints deleted.
217 inline int64_t num_linear_constraints() const;
218
219 // The returned id of the next call to AddLinearConstraint.
220 //
221 // Equal to the number of linear constraints created.
223
224 // Sets the next linear constraint id to be the maximum of
225 // next_linear_constraint_id() and id.
227
228 // Returns true if this id has been created and not yet deleted.
229 inline bool has_linear_constraint(LinearConstraintId id) const;
230
231 // The LinearConstraintsIds in use (not deleted), order not defined.
232 std::vector<LinearConstraintId> LinearConstraints() const;
233
234 // Returns a sorted vector of all existing (not deleted) linear constraints in
235 // the model.
236 //
237 // Runs in O(n log(n)), where n is the number of linear constraints returned.
238 std::vector<LinearConstraintId> SortedLinearConstraints() const;
239
241 // Linear constraint matrix
243
244 // Returns 0.0 if the entry is not in matrix.
245 inline double linear_constraint_coefficient(LinearConstraintId constraint,
246 VariableId variable) const;
248 LinearConstraintId constraint, VariableId variable) const;
249
250 // Setting a value to 0.0 will delete the {constraint, variable} pair from the
251 // underlying sparse matrix representation (and has no effect if the pair is
252 // not present).
254 VariableId variable,
255 double value);
256
257 // The {linear constraint, variable, coefficient} tuples with nonzero linear
258 // constraint matrix coefficients.
259 inline std::vector<std::tuple<LinearConstraintId, VariableId, double>>
261
262 // Returns the variables with nonzero coefficients in a linear constraint.
263 inline std::vector<VariableId> variables_in_linear_constraint(
264 LinearConstraintId constraint) const;
265
266 // Returns the linear constraints with nonzero coefficients on a variable.
267 inline std::vector<LinearConstraintId> linear_constraints_with_variable(
268 VariableId variable) const;
269
271 // Objectives
272 //
273 // The primary ObjectiveId is `PrimaryObjectiveId`. All auxiliary objectives
274 // are referenced by their corresponding `AuxiliaryObjectiveId`.
276
277 inline bool is_maximize(ObjectiveId id) const;
278 inline int64_t objective_priority(ObjectiveId id) const;
279 inline double objective_offset(ObjectiveId id) const;
280 // Returns 0.0 if this variable has no linear objective coefficient.
282 VariableId variable) const;
283 // The ordering of the input variables does not matter.
285 ObjectiveId id, VariableId first_variable,
286 VariableId second_variable) const;
288 ObjectiveId id, VariableId variable) const;
289 // The ordering of the input variables does not matter.
291 ObjectiveId id, VariableId first_variable,
292 VariableId second_variable) const;
293 inline absl::string_view objective_name(ObjectiveId id) const;
294
295 inline void set_is_maximize(ObjectiveId id, bool is_maximize);
296 inline void set_maximize(ObjectiveId id);
297 inline void set_minimize(ObjectiveId id);
298 inline void set_objective_priority(ObjectiveId id, int64_t value);
299 inline void set_objective_offset(ObjectiveId id, double value);
300
301 // Setting a value to 0.0 will delete the variable from the underlying sparse
302 // representation (and has no effect if the variable is not present).
304 VariableId variable,
305 double value);
306 // Setting a value to 0.0 will delete the variable pair from the underlying
307 // sparse representation (and has no effect if the pair is not present).
308 // The ordering of the input variables does not matter.
310 VariableId first_variable,
311 VariableId second_variable,
312 double value);
313
314 // Equivalent to calling set_linear_objective_coefficient(v, 0.0) for every
315 // variable with nonzero objective coefficient, and setting the offset and
316 // quadratic terms to zero as well. Does not effect priority or direction.
317 //
318 // Runs in O(# nonzero linear/quadratic objective terms).
319 inline void clear_objective(ObjectiveId id);
320
321 // The variables with nonzero linear objective coefficients.
322 ABSL_DEPRECATED("Use LinearObjectiveNonzeros instead")
323 inline const absl::flat_hash_map<VariableId, double>& linear_objective(
324 ObjectiveId id) const;
325
326 // Returns the variable ids where the objective has a nonzero linear
327 // objective coefficient in an arbitrary order.
329
330 inline int64_t num_linear_objective_terms(ObjectiveId id) const;
331
332 inline int64_t num_quadratic_objective_terms(ObjectiveId id) const;
333
334 // The variable pairs with nonzero quadratic objective coefficients. The keys
335 // are ordered such that .first <= .second. All values are nonempty.
336 //
337 // TODO(b/233630053) do no allocate the result, expose an iterator API.
338 inline std::vector<std::tuple<VariableId, VariableId, double>>
340
342 // Auxiliary objectives
344
345 // Adds an auxiliary objective to the model and returns its id.
346 //
347 // The returned ids begin at zero and increase by one with each call to
348 // AddAuxiliaryObjective. Deleted ids are NOT reused. If no auxiliary
349 // objectives are deleted, the ids in the model will be consecutive.
350 //
351 // Objectives are minimized by default; you can change the sense via, e.g.,
352 // `set_is_maximize()`.
354 int64_t priority, absl::string_view name = "");
355
356 // Removes an auxiliary objective from the model.
357 //
358 // It is an error to use a deleted auxiliary objective id as input to any
359 // subsequent function calls on the model. Runs in O(#variables in the
360 // auxiliary objective).
362
363 // The number of auxiliary objectives in the model.
364 //
365 // Equal to the number of auxiliary objectives created minus the number of
366 // auxiliary objectives deleted.
367 inline int num_auxiliary_objectives() const;
368
369 // The returned id of the next call to AddAuxiliaryObjective.
370 //
371 // Equal to the number of auxiliary objectives created.
373
374 // Sets the next auxiliary objective id to be the maximum of
375 // next_auxiliary_objective_id() and id.
378
379 // Returns true if this id has been created and not yet deleted.
380 inline bool has_auxiliary_objective(AuxiliaryObjectiveId id) const;
381
382 // The AuxiliaryObjectiveIds in use (not deleted), order not defined.
383 inline std::vector<AuxiliaryObjectiveId> AuxiliaryObjectives() const;
384
385 // Returns a sorted vector of all existing (not deleted) auxiliary objectives
386 // in the model.
387 //
388 // Runs in O(n log(n)), where n is the number of auxiliary objectives
389 // returned.
390 inline std::vector<AuxiliaryObjectiveId> SortedAuxiliaryObjectives() const;
391
393 // Atomic Constraints
394 //
395 // These methods do not directly require template specializations to add
396 // support for new constraint families; this should be handled automatically
397 // upon adding a specialization for `AtomicConstraintTraits`.
399
400 // Adds an atomic constraint to the model and returns its id.
401 //
402 // The returned ids begin at zero and increase by one with each call to
403 // `AddAtomicConstraint<ConstraintData>`. Deleted ids are NOT reused. Callers
404 // may use `ensure_next_constraint_id_at_least<ConstraintData>` to configure
405 // custom indices.
406 template <typename ConstraintData>
407 inline typename ConstraintData::IdType AddAtomicConstraint(
408 ConstraintData data);
409
410 // Removes an atomic constraint from the model.
411 //
412 // It is an error to use a deleted constraint id as input to any subsequent
413 // function calls on the model. Runs in O(#variables in the constraint).
414 template <typename IdType>
415 inline void DeleteAtomicConstraint(IdType id);
416
417 // Accesses the data object that fully represents a single atomic constraint.
418 template <typename IdType>
419 ABSL_DEPRECATED("Prefer GetConstraintData")
420 inline const
421 typename AtomicConstraintTraits<IdType>::ConstraintData& constraint_data(
422 IdType id) const;
423
424 template <typename IdType>
425 inline typename AtomicConstraintTraits<IdType>::ConstraintData
426 GetConstraintData(IdType id) const;
427
428 // Returns the number of atomic constraints in the model of the family
429 // corresponding to `ConstraintData`.
430 //
431 // Equal to the number of such constraints created minus the number of such
432 // constraints deleted.
433 template <typename IdType>
434 inline int64_t num_constraints() const;
435
436 // Returns the smallest valid ID for a new atomic constraint of the family
437 // corresponding to `ConstraintData`.
438 template <typename IdType>
439 inline IdType next_constraint_id() const;
440
441 // Sets the next atomic constraint id of the family corresponding to
442 // `ConstraintData` to be the maximum of
443 // `next_constraint_id<ConstraintData>()` and `id`.
444 template <typename IdType>
445 inline void ensure_next_constraint_id_at_least(IdType id);
446
447 // Returns true if this id has been created and not yet deleted.
448 template <typename IdType>
449 inline bool has_constraint(IdType id) const;
450
451 // Returns the constraint IDs in use (not deleted); order is not defined.
452 template <typename IdType>
453 std::vector<IdType> Constraints() const;
454
455 // Returns a sorted vector of all existing (not deleted) atomic
456 // constraints in the model of the family corresponding to
457 // `ConstraintData`.
458 //
459 // Runs in O(n log(n)), where n is the number of constraints returned.
460 template <typename IdType>
461 std::vector<IdType> SortedConstraints() const;
462
463 // Returns the constraint in the given family in which the variable appears
464 // structurally (i.e., has a coefficient, possibly zero). Order is not
465 // defined.
466 template <typename IdType>
467 ABSL_DEPRECATED("Will be deleted when Elemental turns on")
468 inline std::vector<IdType> ConstraintsWithVariable(
469 VariableId variable_id) const;
470
471 // Returns the variables appearing in the constraint. Order is not defined.
472 template <typename IdType>
473 ABSL_DEPRECATED("Will be deleted when Elemental turns on")
474 inline std::vector<VariableId> VariablesInConstraint(IdType id) const;
475
477 // Export
479
480 // Returns a proto representation of the optimization model.
481 //
482 // Returns an error if the model is too large to fit in a proto (requires
483 // putting more than 2**31 -1 elements in a RepeatedField).
484 //
485 // See FromModelProto() to build a ModelStorage from a proto.
486 absl::StatusOr<ModelProto> ExportModelV2(bool remove_names = false) const;
487
488 ABSL_DEPRECATED("Use ExportModelV2 instead")
489 ModelProto ExportModel(const bool remove_names = false) const {
490 auto result = ExportModelV2(remove_names);
491 CHECK_OK(result);
492 return *std::move(result);
493 }
494
495 // Creates a tracker that can be used to generate a ModelUpdateProto with the
496 // updates that happened since the last checkpoint. The tracker initial
497 // checkpoint corresponds to the current state of the model.
498 //
499 // Thread-safety: this method must not be used while modifying the
500 // ModelStorage. The user is expected to use proper synchronization primitive
501 // to serialize changes to the model and trackers creations. That said
502 // multiple trackers can be created concurrently.
503 //
504 // For each update tracker we define a checkpoint that is the starting point
505 // used to compute the ModelUpdateProto.
506 UpdateTrackerId NewUpdateTracker();
507
508 // Deletes the input tracker.
509 //
510 // It must not be used anymore after its destruction. It can be deleted once,
511 // trying to delete it a second time or use it will raise an assertion
512 // (CHECK).
513 //
514 // The update trackers are automatically deleted when the ModelStorage is
515 // destroyed. Calling this function is thus only useful for performance
516 // reasons, to ensure the ModelStorage does not keep data for update trackers
517 // that are not needed anymore.
518 //
519 // Thread-safety: this method can be called at any time, even during the
520 // creation of other trackers or during model modification. It must not be
521 // called concurrently with ExportModelUpdate() or AdvanceCheckpoint() though.
522 void DeleteUpdateTracker(UpdateTrackerId update_tracker);
523
524 // Returns a proto representation of the changes to the model since the most
525 // recent checkpoint (i.e. last time AdvanceCheckpoint() was called); nullopt
526 // if the update would have been empty.
527 //
528 // Thread-safety: this method must not be used while modifying the
529 // ModelStorage or after calling DeleteUpdateTracker(). The user is expected
530 // to use proper synchronization primitive to serialize changes to the model
531 // and the use of this method.
532 //
533 // It can be called concurrently for different update trackers though.
534 absl::StatusOr<std::optional<ModelUpdateProto>> ExportModelUpdateV2(
535 UpdateTrackerId update_tracker, bool remove_names = false) const;
536
537 ABSL_DEPRECATED("Use ExportUpdateModelV2 instead")
538 std::optional<ModelUpdateProto> ExportModelUpdate(
539 const UpdateTrackerId update_tracker,
540 const bool remove_names = false) const {
541 auto result = ExportModelUpdateV2(update_tracker, remove_names);
542 CHECK_OK(result);
543 return *std::move(result);
544 }
545
546 // Uses the current model state as the starting point to calculate the
547 // ModelUpdateProto next time ExportModelUpdate() is called.
548 //
549 // Thread-safety: this method must not be used while modifying the
550 // ModelStorage or after calling DeleteUpdateTracker(). The user is expected
551 // to use proper synchronization primitive to serialize changes to the model
552 // and the use of this method.
553 //
554 // It can be called concurrently for different update trackers though.
555 void AdvanceCheckpoint(UpdateTrackerId update_tracker);
556
557 // Apply the provided update to this model. Returns a failure if the update is
558 // not valid.
559 //
560 // As with FromModelProto(), duplicated names are ignored.
561 //
562 // It takes O(num_variables + num_constraints) extra memory and execution to
563 // apply the update (due to the need to build a ModelSummary). So even a small
564 // update will have some cost.
565 absl::Status ApplyUpdateProto(const ModelUpdateProto& update_proto);
566
567 private:
568 explicit ModelStorageV2(Elemental elemental)
569 : elemental_(std::move(elemental)) {
570 CHECK_EQ(elemental_.NumDiffs(), 0);
571 }
572
573 Elemental elemental_;
574};
575
578// Inlined function implementations
581
582ModelStorageV2::ModelStorageV2(const absl::string_view model_name,
583 const absl::string_view primary_objective_name)
584 : elemental_(std::string(model_name), std::string(primary_objective_name)) {
585}
586
588// Variables
589
590
591VariableId ModelStorageV2::AddVariable(const absl::string_view name) {
592 return VariableId{
593 elemental_.AddElement<ElementType::kVariable>(name).value()};
594}
595
596VariableId ModelStorageV2::AddVariable(const double lower_bound,
597 const double upper_bound,
598 const bool is_integer,
599 const absl::string_view name) {
601 set_variable_lower_bound(id, lower_bound);
602 set_variable_upper_bound(id, upper_bound);
603 set_variable_is_integer(id, is_integer);
604 return id;
605}
606
608 return elemental_.GetAttr(DoubleAttr1::kVarLb, AttrKey(id.value()));
609}
610
612 return elemental_.GetAttr(DoubleAttr1::kVarUb, AttrKey(id.value()));
613}
614
616 return elemental_.GetAttr(BoolAttr1::kVarInteger, AttrKey(id.value()));
617}
618
619absl::string_view ModelStorageV2::variable_name(const VariableId id) const {
620 const auto name = elemental_.GetElementName(id);
621 CHECK_OK(name);
622 return *name;
623}
624
626 const double lower_bound) {
627 elemental_.SetAttr(DoubleAttr1::kVarLb, AttrKey(id.value()), lower_bound);
628}
629
631 const double upper_bound) {
632 elemental_.SetAttr(DoubleAttr1::kVarUb, AttrKey(id.value()), upper_bound);
633}
634
636 const bool is_integer) {
637 elemental_.SetAttr(BoolAttr1::kVarInteger, AttrKey(id.value()), is_integer);
638}
646}
647
648int64_t ModelStorageV2::num_variables() const {
649 return elemental_.NumElements(ElementType::kVariable);
654}
657 elemental_.EnsureNextElementIdAtLeast(id);
658}
659
661 return elemental_.ElementExists(id);
662}
663
664////////////////////////////////////////////////////////////////////////////////
665// Linear Constraints
667
669 const absl::string_view name) {
670 return elemental_.AddElement<ElementType::kLinearConstraint>(name);
671}
674 const double lower_bound, const double upper_bound,
675 const absl::string_view name) {
679 return id;
683 const LinearConstraintId id) const {
684 return elemental_.GetAttr(DoubleAttr1::kLinConLb, AttrKey(id.value()));
685}
686
688 const LinearConstraintId id) const {
689 return elemental_.GetAttr(DoubleAttr1::kLinConUb, AttrKey(id.value()));
691
693 const LinearConstraintId id) const {
694 const auto name = elemental_.GetElementName(id);
695 CHECK_OK(name);
696 return *name;
697}
698
700 const LinearConstraintId id, const double lower_bound) {
701 elemental_.SetAttr(DoubleAttr1::kLinConLb, AttrKey(id.value()), lower_bound);
702}
703
705 const LinearConstraintId id, const double upper_bound) {
706 elemental_.SetAttr(DoubleAttr1::kLinConUb, AttrKey(id.value()), upper_bound);
707}
708
710 return elemental_.NumElements(ElementType::kLinearConstraint);
711}
712
720 elemental_.EnsureNextElementIdAtLeast(id);
722
724 return elemental_.ElementExists(id);
725}
728// Linear Constraint Matrix
730
732 const LinearConstraintId constraint, const VariableId variable) const {
733 return elemental_.GetAttr(DoubleAttr2::kLinConCoef,
734 AttrKey(constraint.value(), variable.value()));
736
738 const LinearConstraintId constraint, const VariableId variable) const {
739 return elemental_.AttrIsNonDefault(
740 DoubleAttr2::kLinConCoef, AttrKey(constraint.value(), variable.value()));
741}
742
744 const LinearConstraintId constraint, const VariableId variable,
745 const double value) {
746 elemental_.SetAttr(DoubleAttr2::kLinConCoef,
747 AttrKey(constraint.value(), variable.value()), value);
748}
749
750std::vector<std::tuple<LinearConstraintId, VariableId, double>>
752 std::vector<std::tuple<LinearConstraintId, VariableId, double>> result;
753 result.reserve(elemental_.AttrNumNonDefaults(DoubleAttr2::kLinConCoef));
754 for (const auto attr_key :
755 elemental_.AttrNonDefaults(DoubleAttr2::kLinConCoef)) {
756 result.push_back({LinearConstraintId{attr_key[0]}, VariableId{attr_key[1]},
757 elemental_.GetAttr(DoubleAttr2::kLinConCoef, attr_key)});
758 }
759 return result;
760}
763 const LinearConstraintId constraint) const {
764 const std::vector<AttrKeyFor<DoubleAttr2>> slice =
765 elemental_.Slice<0>(DoubleAttr2::kLinConCoef, constraint.value());
766 std::vector<VariableId> result;
767 result.reserve(slice.size());
768 for (const auto key : slice) {
769 result.push_back(VariableId{key[1]});
770 }
771 return result;
772}
773
774std::vector<LinearConstraintId>
776 const VariableId variable) const {
777 const std::vector<AttrKeyFor<DoubleAttr2>> slice =
778 elemental_.Slice<1>(DoubleAttr2::kLinConCoef, variable.value());
779 std::vector<LinearConstraintId> result;
780 result.reserve(slice.size());
781 for (const auto key : slice) {
782 result.push_back(LinearConstraintId{key[0]});
783 }
784 return result;
785}
788// Objectives
790
791bool ModelStorageV2::is_maximize(const ObjectiveId id) const {
792 return id.has_value() ? elemental_.GetAttr(BoolAttr1::kAuxObjMaximize,
793 AttrKey(id->value()))
794 : elemental_.GetAttr(BoolAttr0::kMaximize, AttrKey());
795}
796
797int64_t ModelStorageV2::objective_priority(const ObjectiveId id) const {
798 return id.has_value() ? elemental_.GetAttr(IntAttr1::kAuxObjPriority,
799 AttrKey(id->value()))
800 : elemental_.GetAttr(IntAttr0::kObjPriority, AttrKey());
801}
802
803double ModelStorageV2::objective_offset(const ObjectiveId id) const {
804 return id.has_value()
805 ? elemental_.GetAttr(DoubleAttr1::kAuxObjOffset,
806 AttrKey(id->value()))
807 : elemental_.GetAttr(DoubleAttr0::kObjOffset, AttrKey());
808}
809
811 const ObjectiveId id, const VariableId variable) const {
812 return id.has_value()
813 ? elemental_.GetAttr(DoubleAttr2::kAuxObjLinCoef,
814 AttrKey(id->value(), variable.value()))
815 : elemental_.GetAttr(DoubleAttr1::kObjLinCoef,
816 AttrKey(variable.value()));
818
820 const ObjectiveId id, const VariableId first_variable,
821 const VariableId second_variable) const {
822 CHECK(!id.has_value()) << "multiple objectives not supported";
823 return elemental_.GetAttr(
824 SymmetricDoubleAttr2::kObjQuadCoef,
826 second_variable.value()));
827}
828
830 const ObjectiveId id, const VariableId variable) const {
831 return id.has_value()
832 ? elemental_.AttrIsNonDefault(
833 DoubleAttr2::kAuxObjLinCoef,
834 AttrKey(id->value(), variable.value()))
835 : elemental_.AttrIsNonDefault(DoubleAttr1::kObjLinCoef,
836 AttrKey(variable.value()));
837}
838
840 const ObjectiveId id, const VariableId first_variable,
841 const VariableId second_variable) const {
842 CHECK(!id.has_value()) << "multiple objectives not supported";
843 return elemental_.AttrIsNonDefault(
844 SymmetricDoubleAttr2::kObjQuadCoef,
846 second_variable.value()));
847}
848
849absl::string_view ModelStorageV2::objective_name(const ObjectiveId id) const {
850 if (!id.has_value()) return elemental_.primary_objective_name();
851 const auto name =
852 elemental_.GetElementName(AuxiliaryObjectiveId(id->value()));
853 CHECK_OK(name);
854 return *name;
856
858 const bool is_maximize) {
859 if (id.has_value()) {
860 elemental_.SetAttr(BoolAttr1::kAuxObjMaximize, AttrKey(id->value()),
862 } else {
863 elemental_.SetAttr(BoolAttr0::kMaximize, AttrKey(), is_maximize);
864 }
866
868 set_is_maximize(id, true);
869}
870
872 set_is_maximize(id, false);
873}
874
876 const int64_t value) {
877 if (id.has_value()) {
878 elemental_.SetAttr(IntAttr1::kAuxObjPriority, AttrKey(id->value()), value);
879 } else {
880 elemental_.SetAttr(IntAttr0::kObjPriority, AttrKey(), value);
881 }
882}
885 const double value) {
886 if (id.has_value()) {
887 elemental_.SetAttr(DoubleAttr1::kAuxObjOffset, AttrKey(id->value()), value);
888 } else {
889 elemental_.SetAttr(DoubleAttr0::kObjOffset, AttrKey(), value);
890 }
891}
892
894 const VariableId variable,
895 const double value) {
896 if (id.has_value()) {
897 elemental_.SetAttr(DoubleAttr2::kAuxObjLinCoef,
898 AttrKey(id->value(), variable.value()), value);
899 } else {
900 elemental_.SetAttr(DoubleAttr1::kObjLinCoef, AttrKey(variable.value()),
901 value);
902 }
903}
904
906 const ObjectiveId id, const VariableId first_variable,
907 const VariableId second_variable, const double value) {
908 CHECK(!id.has_value()) << "multiple objectives not supported";
909 elemental_.SetAttr(SymmetricDoubleAttr2::kObjQuadCoef,
910 AttrKeyFor<SymmetricDoubleAttr2>(first_variable.value(),
911 second_variable.value()),
912 value);
913}
914
916 if (id.has_value()) {
917 // TODO(b/372645273): Consider adding a `ResetAttr()` method.
918 elemental_.SetAttr(IntAttr1::kAuxObjPriority, AttrKey(id->value()),
920 elemental_.SetAttr(DoubleAttr1::kAuxObjOffset, AttrKey(id->value()),
922 // TODO(b/372645273): Consider adding a `ClearSlice()` method.
923 for (const auto key :
924 elemental_.Slice<0>(DoubleAttr2::kAuxObjLinCoef, id->value())) {
925 elemental_.SetAttr(DoubleAttr2::kAuxObjLinCoef, key,
927 }
928 } else {
929 elemental_.AttrClear(IntAttr0::kObjPriority);
930 elemental_.AttrClear(DoubleAttr0::kObjOffset);
931 elemental_.AttrClear(DoubleAttr1::kObjLinCoef);
932 }
933}
934
935const absl::flat_hash_map<VariableId, double>& ModelStorageV2::linear_objective(
936 const ObjectiveId id) const {
937 LOG(FATAL) << "cannot be implemented";
938}
939
941 if (id.has_value()) {
942 return elemental_.GetSliceSize<0>(DoubleAttr2::kAuxObjLinCoef, id->value());
943 } else {
944 return elemental_.AttrNumNonDefaults(DoubleAttr1::kObjLinCoef);
945 }
946}
947
949 const ObjectiveId id) const {
950 CHECK(!id.has_value()) << "multiple objectives not supported";
951 return elemental_.AttrNumNonDefaults(SymmetricDoubleAttr2::kObjQuadCoef);
952}
953
954std::vector<std::tuple<VariableId, VariableId, double>>
956 CHECK(!id.has_value()) << "multiple objectives not supported";
957 std::vector<std::tuple<VariableId, VariableId, double>> result;
958 result.reserve(
959 elemental_.AttrNumNonDefaults(SymmetricDoubleAttr2::kObjQuadCoef));
960 for (const auto attr_key :
961 elemental_.AttrNonDefaults(SymmetricDoubleAttr2::kObjQuadCoef)) {
962 result.push_back(
963 {VariableId(attr_key[0]), VariableId(attr_key[1]),
964 elemental_.GetAttr(SymmetricDoubleAttr2::kObjQuadCoef, attr_key)});
965 }
966 return result;
967}
968
970// Auxiliary objectives
972
974 const int64_t priority, const absl::string_view name) {
975 const auto id = elemental_.AddElement<ElementType::kAuxiliaryObjective>(name);
976 elemental_.SetAttr(IntAttr1::kAuxObjPriority, AttrKey(id), priority);
977 return id;
978}
979
981 CHECK(elemental_.DeleteElement(id)) << "no auxiliary objective " << id;
982}
983
985 return static_cast<int>(
987}
988
992}
993
995 const AuxiliaryObjectiveId id) {
996 elemental_.EnsureNextElementIdAtLeast(id);
997}
998
1000 const AuxiliaryObjectiveId id) const {
1001 return elemental_.ElementExists(id);
1002}
1003
1004std::vector<AuxiliaryObjectiveId> ModelStorageV2::AuxiliaryObjectives() const {
1005 const auto ids = elemental_.AllElements<ElementType::kAuxiliaryObjective>();
1006 std::vector<AuxiliaryObjectiveId> result;
1007 result.reserve(ids.size());
1008 for (const auto id : ids) {
1009 result.emplace_back(id);
1010 }
1011 return result;
1013
1014std::vector<AuxiliaryObjectiveId> ModelStorageV2::SortedAuxiliaryObjectives()
1015 const {
1016 auto ids = AuxiliaryObjectives();
1017 absl::c_sort(ids);
1018 return ids;
1019}
1020
1021
1022// Atomic constraint template inline implementations.
1024
1025template <typename ConstraintData>
1026typename ConstraintData::IdType ModelStorageV2::AddAtomicConstraint(
1027 const ConstraintData data) {
1028 if constexpr (!ConstraintData::kSupportsElemental) {
1029 LOG(FATAL) << "elemental not supported yet";
1030 } else {
1031 return internal::AddAtomicConstraint(data, elemental_);
1033}
1034
1035template <typename IdType>
1036void ModelStorageV2::DeleteAtomicConstraint(const IdType id) {
1037 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1038 LOG(FATAL) << "elemental not supported yet";
1039 } else {
1040 CHECK(elemental_.DeleteElement(id))
1041 << "no constraint in the model with id: " << id;
1043}
1044
1045template <typename IdType>
1047ModelStorageV2::GetConstraintData(IdType id) const {
1048 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1049 LOG(FATAL) << "elemental not supported yet";
1050 } else {
1051 return internal::GetAtomicConstraint(id, elemental_);
1052 }
1053}
1054
1055template <typename IdType>
1057ModelStorageV2::constraint_data(const IdType) const {
1058 LOG(FATAL) << "not implementable for ModelStorageV2";
1059}
1060
1061template <typename IdType>
1062int64_t ModelStorageV2::num_constraints() const {
1064 LOG(FATAL) << "elemental not supported yet";
1065 } else {
1066 return elemental_.NumElements(AtomicConstraintTraits<IdType>::kElementType);
1067 }
1068}
1069
1070template <typename IdType>
1072 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1073 LOG(FATAL) << "elemental not supported yet";
1074 } else {
1075 return IdType{
1076 elemental_.NextElementId(AtomicConstraintTraits<IdType>::kElementType)};
1078}
1079
1080template <typename IdType>
1082 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1083 LOG(FATAL) << "elemental not supported yet";
1084 } else {
1085 elemental_.EnsureNextElementIdAtLeast(id);
1086 }
1088
1089template <typename IdType>
1090bool ModelStorageV2::has_constraint(const IdType id) const {
1092 LOG(FATAL) << "elemental not supported yet";
1093 } else {
1094 return elemental_.ElementExists(id);
1095 }
1096}
1097
1098template <typename IdType>
1099std::vector<IdType> ModelStorageV2::Constraints() const {
1100 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1101 LOG(FATAL) << "elemental not supported yet";
1102 } else {
1104 auto els = elemental_.AllElements<e>();
1105 std::vector<ElementId<e>> result;
1106 result.reserve(els.size());
1107 for (const ElementId<e> el : els) {
1108 result.push_back(el);
1109 }
1110 return result;
1112}
1113
1114template <typename IdType>
1115std::vector<IdType> ModelStorageV2::SortedConstraints() const {
1116 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1117 LOG(FATAL) << "elemental not supported yet";
1118 } else {
1119 auto result = Constraints<IdType>();
1120 absl::c_sort(result);
1121 return result;
1122 }
1123}
1124
1125template <typename IdType>
1126std::vector<IdType> ModelStorageV2::ConstraintsWithVariable(
1127 const VariableId) const {
1128 LOG(FATAL) << "not implementable for ModelStorageV2";
1130
1131template <typename IdType>
1132std::vector<VariableId> ModelStorageV2::VariablesInConstraint(
1133 const IdType) const {
1134 LOG(FATAL) << "not implementable for ModelStorageV2";
1135}
1136
1137} // namespace operations_research::math_opt
1138
1139#endif // OR_TOOLS_MATH_OPT_STORAGE_MODEL_STORAGE_V2_H_
A strongly typed element id. Value type.
Definition elements.h:64
int64_t value() const
Returns the raw id value.
Definition elements.h:77
Policy::CheckResultT SetAttr(AttrType a, AttrKeyFor< AttrType > key, ValueTypeFor< AttrType > value)
Definition elemental.h:448
bool ElementExists(const ElementId< e > id) const
Definition elemental.h:110
int64_t AttrNumNonDefaults(const AttrType a) const
Returns the number of keys where a is non-default.
Definition elemental.h:221
const std::string & primary_objective_name() const
The name of the primary objective of this optimization model.
Definition elemental.h:75
absl::StatusOr< absl::string_view > GetElementName(const ElementId< e > id) const
Definition elemental.h:122
bool DeleteElement(const ElementId< e > id)
Definition elemental.h:100
Policy::template Wrapped< bool > AttrIsNonDefault(AttrType a, AttrKeyFor< AttrType > key) const
Definition elemental.h:441
void AttrClear(AttrType a)
Inline and template implementation.
Definition elemental.h:406
Policy::template Wrapped< ValueTypeFor< AttrType > > GetAttr(AttrType a, AttrKeyFor< AttrType > key) const
Definition elemental.h:434
ElementIdsVector< e > AllElements() const
Definition elemental.h:136
void EnsureNextElementIdAtLeast(const ElementId< e > id)
Definition elemental.h:166
ElementId< e > AddElement(const absl::string_view name)
Creates and returns the id of a new element for the element type e.
Definition elemental.h:87
int64_t NextElementId(const ElementType e) const
Definition elemental.h:149
int64_t NumElements(const ElementType e) const
Definition elemental.h:157
void set_linear_constraint_lower_bound(LinearConstraintId id, double lower_bound)
std::vector< VariableId > variables() const
double linear_constraint_lower_bound(LinearConstraintId id) const
bool has_auxiliary_objective(AuxiliaryObjectiveId id) const
Returns true if this id has been created and not yet deleted.
std::vector< VariableId > VariablesInConstraint(IdType id) const
Returns the variables appearing in the constraint. Order is not defined.
void ensure_next_linear_constraint_id_at_least(LinearConstraintId id)
absl::Status ApplyUpdateProto(const ModelUpdateProto &update_proto)
void set_objective_priority(ObjectiveId id, int64_t value)
void DeleteUpdateTracker(UpdateTrackerId update_tracker)
ModelStorageV2(const ModelStorageV2 &)=delete
std::vector< IdType > ConstraintsWithVariable(VariableId variable_id) const
absl::string_view linear_constraint_name(LinearConstraintId id) const
static absl::StatusOr< absl::Nonnull< std::unique_ptr< ModelStorageV2 > > > FromModelProto(const ModelProto &model_proto)
int64_t num_linear_objective_terms(ObjectiveId id) const
const AtomicConstraintTraits< IdType >::ConstraintData & constraint_data(IdType id) const
Accesses the data object that fully represents a single atomic constraint.
void ensure_next_variable_id_at_least(VariableId id)
Sets the next variable id to be the maximum of next_variable_id() and id.
bool has_constraint(IdType id) const
Returns true if this id has been created and not yet deleted.
std::vector< LinearConstraintId > LinearConstraints() const
The LinearConstraintsIds in use (not deleted), order not defined.
void set_linear_constraint_coefficient(LinearConstraintId constraint, VariableId variable, double value)
bool is_quadratic_objective_coefficient_nonzero(ObjectiveId id, VariableId first_variable, VariableId second_variable) const
The ordering of the input variables does not matter.
int64_t objective_priority(ObjectiveId id) const
std::vector< VariableId > Variables() const
The VariableIds in use (not deleted), order not defined.
bool has_linear_constraint(LinearConstraintId id) const
Returns true if this id has been created and not yet deleted.
void set_linear_constraint_upper_bound(LinearConstraintId id, double upper_bound)
void set_quadratic_objective_coefficient(ObjectiveId id, VariableId first_variable, VariableId second_variable, double value)
std::vector< VariableId > LinearObjectiveNonzeros(ObjectiveId id) const
bool has_variable(VariableId id) const
Returns true if this id has been created and not yet deleted.
double linear_objective_coefficient(ObjectiveId id, VariableId variable) const
Returns 0.0 if this variable has no linear objective coefficient.
ModelStorageV2(absl::string_view model_name="", absl::string_view primary_objective_name="")
Creates an empty minimization problem.
LinearConstraintId next_linear_constraint_id() const
void set_variable_lower_bound(VariableId id, double lower_bound)
void set_objective_offset(ObjectiveId id, double value)
int64_t num_quadratic_objective_terms(ObjectiveId id) const
std::vector< AuxiliaryObjectiveId > SortedAuxiliaryObjectives() const
double linear_constraint_coefficient(LinearConstraintId constraint, VariableId variable) const
Returns 0.0 if the entry is not in matrix.
ModelStorageV2 & operator=(const ModelStorageV2 &)=delete
void set_is_maximize(ObjectiveId id, bool is_maximize)
AuxiliaryObjectiveId next_auxiliary_objective_id() const
void ensure_next_auxiliary_objective_id_at_least(AuxiliaryObjectiveId id)
double quadratic_objective_coefficient(ObjectiveId id, VariableId first_variable, VariableId second_variable) const
The ordering of the input variables does not matter.
std::vector< IdType > Constraints() const
Returns the constraint IDs in use (not deleted); order is not defined.
void AdvanceCheckpoint(UpdateTrackerId update_tracker)
VariableId AddVariable(absl::string_view name="")
absl::StatusOr< ModelProto > ExportModelV2(bool remove_names=false) const
absl::string_view variable_name(VariableId id) const
std::vector< LinearConstraintId > SortedLinearConstraints() const
void DeleteLinearConstraint(LinearConstraintId id)
std::vector< VariableId > variables_in_linear_constraint(LinearConstraintId constraint) const
Returns the variables with nonzero coefficients in a linear constraint.
void DeleteAuxiliaryObjective(AuxiliaryObjectiveId id)
void set_variable_upper_bound(VariableId id, double upper_bound)
std::vector< std::tuple< LinearConstraintId, VariableId, double > > linear_constraint_matrix() const
AuxiliaryObjectiveId AddAuxiliaryObjective(int64_t priority, absl::string_view name="")
std::vector< AuxiliaryObjectiveId > AuxiliaryObjectives() const
The AuxiliaryObjectiveIds in use (not deleted), order not defined.
std::optional< ModelUpdateProto > ExportModelUpdate(const UpdateTrackerId update_tracker, const bool remove_names=false) const
bool is_linear_objective_coefficient_nonzero(ObjectiveId id, VariableId variable) const
std::vector< VariableId > SortedVariables() const
std::vector< std::tuple< VariableId, VariableId, double > > quadratic_objective_terms(ObjectiveId id) const
const absl::flat_hash_map< VariableId, double > & linear_objective(ObjectiveId id) const
The variables with nonzero linear objective coefficients.
double linear_constraint_upper_bound(LinearConstraintId id) const
ModelProto ExportModel(const bool remove_names=false) const
void set_variable_is_integer(VariableId id, bool is_integer)
absl::Nonnull< std::unique_ptr< ModelStorageV2 > > Clone(std::optional< absl::string_view > new_name=std::nullopt) const
bool is_linear_constraint_coefficient_nonzero(LinearConstraintId constraint, VariableId variable) const
AtomicConstraintTraits< IdType >::ConstraintData GetConstraintData(IdType id) const
LinearConstraintId AddLinearConstraint(absl::string_view name="")
std::vector< LinearConstraintId > linear_constraints_with_variable(VariableId variable) const
Returns the linear constraints with nonzero coefficients on a variable.
void set_linear_objective_coefficient(ObjectiveId id, VariableId variable, double value)
ConstraintData::IdType AddAtomicConstraint(ConstraintData data)
absl::StatusOr< std::optional< ModelUpdateProto > > ExportModelUpdateV2(UpdateTrackerId update_tracker, bool remove_names=false) const
std::vector< IdType > SortedConstraints() const
absl::string_view objective_name(ObjectiveId id) const
AtomicConstraintTraits< ElementId< e > >::ConstraintData GetAtomicConstraint(ElementId< e > id, const Elemental &elemental)
ConstraintData::IdType AddAtomicConstraint(const ConstraintData &data, Elemental &elemental)
An object oriented wrapper for quadratic constraints in ModelStorage.
Definition gurobi_isv.cc:28
AttrKey< AttrTypeDescriptorT< AttrType >::kNumKeyElements, typename AttrTypeDescriptorT< AttrType >::Symmetry > AttrKeyFor
The type of the AttrKey for attribute type AttrType.
For infeasible and unbounded see Not checked if options check_solutions_if_inf_or_unbounded and the If options first_solution_only is false
problem is infeasible or unbounded (default).
Definition matchers.h:468
ElementId< ElementType::kAuxiliaryObjective > AuxiliaryObjectiveId
Definition elements.h:266
ElementId< ElementType::kVariable > VariableId
Definition elements.h:264
AttrKey(Ints... dims) -> AttrKey< sizeof...(Ints), NoSymmetry >
CTAD for AttrKey(1,2).
ElementId< ElementType::kLinearConstraint > LinearConstraintId
Definition elements.h:265
std::optional< AuxiliaryObjectiveId > ObjectiveId
nullopt denotes the primary objective.
constexpr AttrTypeDescriptorT< decltype(attr)>::ValueType GetAttrDefaultValue()
ABSL_DEPRECATED("Use TerminateForReason(bool, TerminationReasonProto, absl::string_view) " "instead") TerminationProto TerminateForReason(TerminationReasonProto reason
STL namespace.