Google OR-Tools v9.15
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 ORTOOLS_MATH_OPT_STORAGE_MODEL_STORAGE_V2_H_
15#define ORTOOLS_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
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.
72// On bad input:
73//
74// Using a bad variable id or constraint id (an id not in the current model,
75// which includes ids that have been deleted) on any method will result in an
76// immediate failure (either a CHECK failure or an exception, which is an
77// implementation detail you should not rely on). We make no attempt to say if a
78// model is invalid (e.g. a variable lower bound is infinite, exceeds an upper
79// bound, or is NaN). The exported models are validated instead, see
80// model_validator.h.
82 public:
83 // Returns a storage from the input proto. Returns a failure status if the
84 // input proto is invalid.
85 //
86 // Variable/constraint names can be repeated in the input proto but will be
87 // considered invalid when solving.
88 //
89 // See ApplyUpdateProto() for dealing with subsequent updates.
90 static absl::StatusOr<absl_nonnull std::unique_ptr<ModelStorageV2>>
91 FromModelProto(const ModelProto& model_proto);
92
93 // Creates an empty minimization problem.
94 inline explicit ModelStorageV2(absl::string_view model_name = "",
95 absl::string_view primary_objective_name = "");
96
99
100 // Returns a clone of the model, optionally changing model's name.
101 //
102 // The variables and constraints have the same ids. The clone will also not
103 // reused any id of variable/constraint that was deleted in the original.
104 //
105 // Note that the returned model does not have any update tracker.
106 absl_nonnull std::unique_ptr<ModelStorageV2> Clone(
107 std::optional<absl::string_view> new_name = std::nullopt) const;
108
109 inline const std::string& name() const { return elemental_.model_name(); }
110
112 // Variables
114
115 // Adds a continuous unbounded variable to the model and returns its id.
116 //
117 // See AddVariable(double, double, bool, absl::string_view) for details.
118 inline VariableId AddVariable(absl::string_view name = "");
119
120 // Adds a variable to the model and returns its id.
121 //
122 // The returned ids begin at zero and increase by one with each call to
123 // AddVariable. Deleted ids are NOT reused. If no variables are deleted,
124 // the ids in the model will be consecutive.
125 inline VariableId AddVariable(double lower_bound, double upper_bound,
126 bool is_integer, absl::string_view name = "");
127
128 inline double variable_lower_bound(VariableId id) const;
129 inline double variable_upper_bound(VariableId id) const;
130 inline bool is_variable_integer(VariableId id) const;
131 inline absl::string_view variable_name(VariableId id) const;
132
133 inline void set_variable_lower_bound(VariableId id, double lower_bound);
134 inline void set_variable_upper_bound(VariableId id, double upper_bound);
135 inline void set_variable_is_integer(VariableId id, bool is_integer);
136 inline void set_variable_as_integer(VariableId id);
138
139 // Removes a variable from the model.
140 //
141 // It is an error to use a deleted variable id as input to any subsequent
142 // function calls on the model. Runs in O(#constraints containing the
143 // variable).
144 void DeleteVariable(VariableId id);
145
146 // The number of variables in the model.
147 //
148 // Equal to the number of variables created minus the number of variables
149 // deleted.
150 inline int64_t num_variables() const;
151
152 // The returned id of the next call to AddVariable.
153 //
154 // Equal to the number of variables created.
155 inline VariableId next_variable_id() const;
156
157 // Sets the next variable id to be the maximum of next_variable_id() and id.
159
160 // Returns true if this id has been created and not yet deleted.
161 inline bool has_variable(VariableId id) const;
162
163 ABSL_DEPRECATED("Use Variables() instead")
164 std::vector<VariableId> variables() const { return Variables(); }
165
166 // The VariableIds in use (not deleted), order not defined.
167 std::vector<VariableId> Variables() const;
168
169 // Returns a sorted vector of all existing (not deleted) variables in the
170 // model.
171 //
172 // Runs in O(n log(n)), where n is the number of variables returned.
173 std::vector<VariableId> SortedVariables() const;
174
176 // Linear Constraints
178
179 // Adds a linear constraint to the model with a lower bound of -inf and an
180 // upper bound of +inf and returns its id.
181 //
182 // See AddLinearConstraint(double, double, absl::string_view) for details.
183 inline LinearConstraintId AddLinearConstraint(absl::string_view name = "");
184
185 // Adds a linear constraint to the model returns its id.
186 //
187 // The returned ids begin at zero and increase by one with each call to
188 // AddLinearConstraint. Deleted ids are NOT reused. If no linear
189 // constraints are deleted, the ids in the model will be consecutive.
190 inline LinearConstraintId AddLinearConstraint(double lower_bound,
191 double upper_bound,
192 absl::string_view name = "");
193
196 inline absl::string_view linear_constraint_name(LinearConstraintId id) const;
197
199 double lower_bound);
201 double upper_bound);
202
203 // Removes a linear constraint from the model.
204 //
205 // It is an error to use a deleted linear constraint id as input to any
206 // subsequent function calls on the model. Runs in O(#variables in the linear
207 // constraint).
209
210 // The number of linear constraints in the model.
211 //
212 // Equal to the number of linear constraints created minus the number of
213 // linear constraints deleted.
214 inline int64_t num_linear_constraints() const;
215
216 // The returned id of the next call to AddLinearConstraint.
217 //
218 // Equal to the number of linear constraints created.
220
221 // Sets the next linear constraint id to be the maximum of
222 // next_linear_constraint_id() and id.
224
225 // Returns true if this id has been created and not yet deleted.
226 inline bool has_linear_constraint(LinearConstraintId id) const;
227
228 // The LinearConstraintsIds in use (not deleted), order not defined.
229 std::vector<LinearConstraintId> LinearConstraints() const;
230
231 // Returns a sorted vector of all existing (not deleted) linear constraints in
232 // the model.
233 //
234 // Runs in O(n log(n)), where n is the number of linear constraints returned.
235 std::vector<LinearConstraintId> SortedLinearConstraints() const;
236
238 // Linear constraint matrix
240
241 // Returns 0.0 if the entry is not in matrix.
242 inline double linear_constraint_coefficient(LinearConstraintId constraint,
243 VariableId variable) const;
245 LinearConstraintId constraint, VariableId variable) const;
246
247 // Setting a value to 0.0 will delete the {constraint, variable} pair from the
248 // underlying sparse matrix representation (and has no effect if the pair is
249 // not present).
251 VariableId variable,
252 double value);
253
254 // The {linear constraint, variable, coefficient} tuples with nonzero linear
255 // constraint matrix coefficients.
256 inline std::vector<std::tuple<LinearConstraintId, VariableId, double>>
258
259 // Returns the variables with nonzero coefficients in a linear constraint.
260 inline std::vector<VariableId> variables_in_linear_constraint(
261 LinearConstraintId constraint) const;
262
263 // Returns the linear constraints with nonzero coefficients on a variable.
264 inline std::vector<LinearConstraintId> linear_constraints_with_variable(
265 VariableId variable) const;
266
268 // Objectives
269 //
270 // The primary ObjectiveId is `PrimaryObjectiveId`. All auxiliary objectives
271 // are referenced by their corresponding `AuxiliaryObjectiveId`.
273
274 inline bool is_maximize(ObjectiveId id) const;
275 inline int64_t objective_priority(ObjectiveId id) const;
276 inline double objective_offset(ObjectiveId id) const;
277 // Returns 0.0 if this variable has no linear objective coefficient.
279 VariableId variable) const;
280 // The ordering of the input variables does not matter.
282 ObjectiveId id, VariableId first_variable,
283 VariableId second_variable) const;
285 ObjectiveId id, VariableId variable) const;
286 // The ordering of the input variables does not matter.
288 ObjectiveId id, VariableId first_variable,
289 VariableId second_variable) const;
290 inline absl::string_view objective_name(ObjectiveId id) const;
291
292 inline void set_is_maximize(ObjectiveId id, bool is_maximize);
293 inline void set_maximize(ObjectiveId id);
294 inline void set_minimize(ObjectiveId id);
295 inline void set_objective_priority(ObjectiveId id, int64_t value);
296 inline void set_objective_offset(ObjectiveId id, double value);
297
298 // Setting a value to 0.0 will delete the variable from the underlying sparse
299 // representation (and has no effect if the variable is not present).
301 VariableId variable,
302 double value);
303 // Setting a value to 0.0 will delete the variable pair from the underlying
304 // sparse representation (and has no effect if the pair is not present).
305 // The ordering of the input variables does not matter.
307 VariableId first_variable,
308 VariableId second_variable,
309 double value);
310
311 // Equivalent to calling set_linear_objective_coefficient(v, 0.0) for every
312 // variable with nonzero objective coefficient, and setting the offset and
313 // quadratic terms to zero as well. Does not effect priority or direction.
314 //
315 // Runs in O(# nonzero linear/quadratic objective terms).
316 inline void clear_objective(ObjectiveId id);
317
318 // The variables with nonzero linear objective coefficients.
319 ABSL_DEPRECATED("Use LinearObjectiveNonzeros instead")
320 inline const absl::flat_hash_map<VariableId, double>& linear_objective(
321 ObjectiveId id) const;
322
323 // Returns the variable ids where the objective has a nonzero linear
324 // objective coefficient in an arbitrary order.
326
327 inline int64_t num_linear_objective_terms(ObjectiveId id) const;
328
329 inline int64_t num_quadratic_objective_terms(ObjectiveId id) const;
330
331 // The variable pairs with nonzero quadratic objective coefficients. The keys
332 // are ordered such that .first <= .second. All values are nonempty.
333 //
334 // TODO(b/233630053) do no allocate the result, expose an iterator API.
335 inline std::vector<std::tuple<VariableId, VariableId, double>>
337
339 // Auxiliary objectives
341
342 // Adds an auxiliary objective to the model and returns its id.
343 //
344 // The returned ids begin at zero and increase by one with each call to
345 // AddAuxiliaryObjective. Deleted ids are NOT reused. If no auxiliary
346 // objectives are deleted, the ids in the model will be consecutive.
347 //
348 // Objectives are minimized by default; you can change the sense via, e.g.,
349 // `set_is_maximize()`.
351 int64_t priority, absl::string_view name = "");
352
353 // Removes an auxiliary objective from the model.
354 //
355 // It is an error to use a deleted auxiliary objective id as input to any
356 // subsequent function calls on the model. Runs in O(#variables in the
357 // auxiliary objective).
359
360 // The number of auxiliary objectives in the model.
361 //
362 // Equal to the number of auxiliary objectives created minus the number of
363 // auxiliary objectives deleted.
364 inline int num_auxiliary_objectives() const;
365
366 // The returned id of the next call to AddAuxiliaryObjective.
367 //
368 // Equal to the number of auxiliary objectives created.
370
371 // Sets the next auxiliary objective id to be the maximum of
372 // next_auxiliary_objective_id() and id.
375
376 // Returns true if this id has been created and not yet deleted.
377 inline bool has_auxiliary_objective(AuxiliaryObjectiveId id) const;
378
379 // The AuxiliaryObjectiveIds in use (not deleted), order not defined.
380 inline std::vector<AuxiliaryObjectiveId> AuxiliaryObjectives() const;
381
382 // Returns a sorted vector of all existing (not deleted) auxiliary objectives
383 // in the model.
384 //
385 // Runs in O(n log(n)), where n is the number of auxiliary objectives
386 // returned.
387 inline std::vector<AuxiliaryObjectiveId> SortedAuxiliaryObjectives() const;
388
390 // Atomic Constraints
391 //
392 // These methods do not directly require template specializations to add
393 // support for new constraint families; this should be handled automatically
394 // upon adding a specialization for `AtomicConstraintTraits`.
396
397 // Adds an atomic constraint to the model and returns its id.
398 //
399 // The returned ids begin at zero and increase by one with each call to
400 // `AddAtomicConstraint<ConstraintData>`. Deleted ids are NOT reused. Callers
401 // may use `ensure_next_constraint_id_at_least<ConstraintData>` to configure
402 // custom indices.
403 template <typename ConstraintData>
404 inline typename ConstraintData::IdType AddAtomicConstraint(
405 ConstraintData data);
406
407 // Removes an atomic constraint from the model.
408 //
409 // It is an error to use a deleted constraint id as input to any subsequent
410 // function calls on the model. Runs in O(#variables in the constraint).
411 template <typename IdType>
412 inline void DeleteAtomicConstraint(IdType id);
413
414 // Accesses the data object that fully represents a single atomic constraint.
415 template <typename IdType>
416 ABSL_DEPRECATED("Prefer GetConstraintData")
417 inline const
418 typename AtomicConstraintTraits<IdType>::ConstraintData& constraint_data(
419 IdType id) const;
420
421 template <typename IdType>
422 inline typename AtomicConstraintTraits<IdType>::ConstraintData
423 GetConstraintData(IdType id) const;
424
425 // Returns the number of atomic constraints in the model of the family
426 // corresponding to `ConstraintData`.
427 //
428 // Equal to the number of such constraints created minus the number of such
429 // constraints deleted.
430 template <typename IdType>
431 inline int64_t num_constraints() const;
432
433 // Returns the smallest valid ID for a new atomic constraint of the family
434 // corresponding to `ConstraintData`.
435 template <typename IdType>
436 inline IdType next_constraint_id() const;
437
438 // Sets the next atomic constraint id of the family corresponding to
439 // `ConstraintData` to be the maximum of
440 // `next_constraint_id<ConstraintData>()` and `id`.
441 template <typename IdType>
442 inline void ensure_next_constraint_id_at_least(IdType id);
443
444 // Returns true if this id has been created and not yet deleted.
445 template <typename IdType>
446 inline bool has_constraint(IdType id) const;
447
448 // Returns the constraint IDs in use (not deleted); order is not defined.
449 template <typename IdType>
450 std::vector<IdType> Constraints() const;
451
452 // Returns a sorted vector of all existing (not deleted) atomic
453 // constraints in the model of the family corresponding to
454 // `ConstraintData`.
455 //
456 // Runs in O(n log(n)), where n is the number of constraints returned.
457 template <typename IdType>
458 std::vector<IdType> SortedConstraints() const;
459
460 // Returns the constraint in the given family in which the variable appears
461 // structurally (i.e., has a coefficient, possibly zero). Order is not
462 // defined.
463 template <typename IdType>
464 ABSL_DEPRECATED("Will be deleted when Elemental turns on")
465 inline std::vector<IdType> ConstraintsWithVariable(
466 VariableId variable_id) const;
467
468 // Returns the variables appearing in the constraint. Order is not defined.
469 template <typename IdType>
470 ABSL_DEPRECATED("Will be deleted when Elemental turns on")
471 inline std::vector<VariableId> VariablesInConstraint(IdType id) const;
472
474 // Export
476
477 // Returns a proto representation of the optimization model.
478 //
479 // Returns an error if the model is too large to fit in a proto (requires
480 // putting more than 2**31 -1 elements in a RepeatedField).
481 //
482 // See FromModelProto() to build a ModelStorage from a proto.
483 absl::StatusOr<ModelProto> ExportModelV2(bool remove_names = false) const;
484
485 ABSL_DEPRECATED("Use ExportModelV2 instead")
486 ModelProto ExportModel(const bool remove_names = false) const {
487 auto result = ExportModelV2(remove_names);
488 CHECK_OK(result);
489 return *std::move(result);
490 }
491
492 // Creates a tracker that can be used to generate a ModelUpdateProto with the
493 // updates that happened since the last checkpoint. The tracker initial
494 // checkpoint corresponds to the current state of the model.
495 //
496 // Thread-safety: this method must not be used while modifying the
497 // ModelStorage. The user is expected to use proper synchronization primitive
498 // to serialize changes to the model and trackers creations. That said
499 // multiple trackers can be created concurrently.
500 //
501 // For each update tracker we define a checkpoint that is the starting point
502 // used to compute the ModelUpdateProto.
503 UpdateTrackerId NewUpdateTracker();
504
505 // Deletes the input tracker.
506 //
507 // It must not be used anymore after its destruction. It can be deleted once,
508 // trying to delete it a second time or use it will raise an assertion
509 // (CHECK).
510 //
511 // The update trackers are automatically deleted when the ModelStorage is
512 // destroyed. Calling this function is thus only useful for performance
513 // reasons, to ensure the ModelStorage does not keep data for update trackers
514 // that are not needed anymore.
515 //
516 // Thread-safety: this method can be called at any time, even during the
517 // creation of other trackers or during model modification. It must not be
518 // called concurrently with ExportModelUpdate() or AdvanceCheckpoint() though.
519 void DeleteUpdateTracker(UpdateTrackerId update_tracker);
520
521 // Returns a proto representation of the changes to the model since the most
522 // recent checkpoint (i.e. last time AdvanceCheckpoint() was called); nullopt
523 // if the update would have been empty.
524 //
525 // Thread-safety: this method must not be used while modifying the
526 // ModelStorage or after calling DeleteUpdateTracker(). The user is expected
527 // to use proper synchronization primitive to serialize changes to the model
528 // and the use of this method.
529 //
530 // It can be called concurrently for different update trackers though.
531 absl::StatusOr<std::optional<ModelUpdateProto>> ExportModelUpdateV2(
532 UpdateTrackerId update_tracker, bool remove_names = false) const;
533
534 ABSL_DEPRECATED("Use ExportUpdateModelV2 instead")
536 const UpdateTrackerId update_tracker,
537 const bool remove_names = false) const {
538 auto result = ExportModelUpdateV2(update_tracker, remove_names);
539 CHECK_OK(result);
540 return *std::move(result);
541 }
542
543 // Uses the current model state as the starting point to calculate the
544 // ModelUpdateProto next time ExportModelUpdate() is called.
545 //
546 // Thread-safety: this method must not be used while modifying the
547 // ModelStorage or after calling DeleteUpdateTracker(). The user is expected
548 // to use proper synchronization primitive to serialize changes to the model
549 // and the use of this method.
550 //
551 // It can be called concurrently for different update trackers though.
552 void AdvanceCheckpoint(UpdateTrackerId update_tracker);
553
554 // Apply the provided update to this model. Returns a failure if the update is
555 // not valid.
556 //
557 // As with FromModelProto(), duplicated names are ignored.
558 //
559 // It takes O(num_variables + num_constraints) extra memory and execution to
560 // apply the update (due to the need to build a ModelSummary). So even a small
561 // update will have some cost.
562 absl::Status ApplyUpdateProto(const ModelUpdateProto& update_proto);
563
564 private:
565 explicit ModelStorageV2(Elemental elemental)
566 : elemental_(std::move(elemental)) {
567 CHECK_EQ(elemental_.NumDiffs(), 0);
568 }
569
570 Elemental elemental_;
571};
572
575// Inlined function implementations
578
579ModelStorageV2::ModelStorageV2(const absl::string_view model_name,
580 const absl::string_view primary_objective_name)
581 : elemental_(std::string(model_name), std::string(primary_objective_name)) {
582}
583
585// Variables
586
587
588VariableId ModelStorageV2::AddVariable(const absl::string_view name) {
589 return VariableId{
590 elemental_.AddElement<ElementType::kVariable>(name).value()};
591}
592
593VariableId ModelStorageV2::AddVariable(const double lower_bound,
594 const double upper_bound,
595 const bool is_integer,
596 const absl::string_view name) {
598 set_variable_lower_bound(id, lower_bound);
599 set_variable_upper_bound(id, upper_bound);
600 set_variable_is_integer(id, is_integer);
601 return id;
602}
603
605 return elemental_.GetAttr(DoubleAttr1::kVarLb, AttrKey(id.value()));
606}
607
609 return elemental_.GetAttr(DoubleAttr1::kVarUb, AttrKey(id.value()));
610}
611
613 return elemental_.GetAttr(BoolAttr1::kVarInteger, AttrKey(id.value()));
614}
615
616absl::string_view ModelStorageV2::variable_name(const VariableId id) const {
617 const auto name = elemental_.GetElementName(id);
618 CHECK_OK(name);
619 return *name;
620}
621
623 const double lower_bound) {
624 elemental_.SetAttr(DoubleAttr1::kVarLb, AttrKey(id.value()), lower_bound);
625}
626
628 const double upper_bound) {
629 elemental_.SetAttr(DoubleAttr1::kVarUb, AttrKey(id.value()), upper_bound);
630}
631
633 const bool is_integer) {
634 elemental_.SetAttr(BoolAttr1::kVarInteger, AttrKey(id.value()), is_integer);
635}
643}
644
645int64_t ModelStorageV2::num_variables() const {
646 return elemental_.NumElements(ElementType::kVariable);
651}
654 elemental_.EnsureNextElementIdAtLeast(id);
655}
656
658 return elemental_.ElementExists(id);
659}
660
661////////////////////////////////////////////////////////////////////////////////
662// Linear Constraints
664
666 const absl::string_view name) {
667 return elemental_.AddElement<ElementType::kLinearConstraint>(name);
668}
671 const double lower_bound, const double upper_bound,
672 const absl::string_view name) {
676 return id;
680 const LinearConstraintId id) const {
681 return elemental_.GetAttr(DoubleAttr1::kLinConLb, AttrKey(id.value()));
682}
683
685 const LinearConstraintId id) const {
686 return elemental_.GetAttr(DoubleAttr1::kLinConUb, AttrKey(id.value()));
688
690 const LinearConstraintId id) const {
691 const auto name = elemental_.GetElementName(id);
692 CHECK_OK(name);
693 return *name;
694}
695
697 const LinearConstraintId id, const double lower_bound) {
698 elemental_.SetAttr(DoubleAttr1::kLinConLb, AttrKey(id.value()), lower_bound);
699}
700
702 const LinearConstraintId id, const double upper_bound) {
703 elemental_.SetAttr(DoubleAttr1::kLinConUb, AttrKey(id.value()), upper_bound);
704}
705
707 return elemental_.NumElements(ElementType::kLinearConstraint);
708}
709
717 elemental_.EnsureNextElementIdAtLeast(id);
719
721 return elemental_.ElementExists(id);
722}
725// Linear Constraint Matrix
727
729 const LinearConstraintId constraint, const VariableId variable) const {
730 return elemental_.GetAttr(DoubleAttr2::kLinConCoef,
731 AttrKey(constraint.value(), variable.value()));
733
735 const LinearConstraintId constraint, const VariableId variable) const {
736 return elemental_.AttrIsNonDefault(
737 DoubleAttr2::kLinConCoef, AttrKey(constraint.value(), variable.value()));
738}
739
741 const LinearConstraintId constraint, const VariableId variable,
742 const double value) {
743 elemental_.SetAttr(DoubleAttr2::kLinConCoef,
744 AttrKey(constraint.value(), variable.value()), value);
745}
746
747std::vector<std::tuple<LinearConstraintId, VariableId, double>>
749 std::vector<std::tuple<LinearConstraintId, VariableId, double>> result;
750 result.reserve(elemental_.AttrNumNonDefaults(DoubleAttr2::kLinConCoef));
751 for (const auto attr_key :
752 elemental_.AttrNonDefaults(DoubleAttr2::kLinConCoef)) {
753 result.push_back({LinearConstraintId{attr_key[0]}, VariableId{attr_key[1]},
754 elemental_.GetAttr(DoubleAttr2::kLinConCoef, attr_key)});
755 }
756 return result;
757}
760 const LinearConstraintId constraint) const {
761 const std::vector<AttrKeyFor<DoubleAttr2>> slice =
762 elemental_.Slice<0>(DoubleAttr2::kLinConCoef, constraint.value());
763 std::vector<VariableId> result;
764 result.reserve(slice.size());
765 for (const auto key : slice) {
766 result.push_back(VariableId{key[1]});
767 }
768 return result;
769}
770
771std::vector<LinearConstraintId>
773 const VariableId variable) const {
774 const std::vector<AttrKeyFor<DoubleAttr2>> slice =
775 elemental_.Slice<1>(DoubleAttr2::kLinConCoef, variable.value());
776 std::vector<LinearConstraintId> result;
777 result.reserve(slice.size());
778 for (const auto key : slice) {
779 result.push_back(LinearConstraintId{key[0]});
780 }
781 return result;
782}
785// Objectives
787
788bool ModelStorageV2::is_maximize(const ObjectiveId id) const {
789 return id.has_value() ? elemental_.GetAttr(BoolAttr1::kAuxObjMaximize,
790 AttrKey(id->value()))
791 : elemental_.GetAttr(BoolAttr0::kMaximize, AttrKey());
792}
793
794int64_t ModelStorageV2::objective_priority(const ObjectiveId id) const {
795 return id.has_value() ? elemental_.GetAttr(IntAttr1::kAuxObjPriority,
796 AttrKey(id->value()))
797 : elemental_.GetAttr(IntAttr0::kObjPriority, AttrKey());
798}
799
800double ModelStorageV2::objective_offset(const ObjectiveId id) const {
801 return id.has_value()
802 ? elemental_.GetAttr(DoubleAttr1::kAuxObjOffset,
803 AttrKey(id->value()))
804 : elemental_.GetAttr(DoubleAttr0::kObjOffset, AttrKey());
805}
806
808 const ObjectiveId id, const VariableId variable) const {
809 return id.has_value()
810 ? elemental_.GetAttr(DoubleAttr2::kAuxObjLinCoef,
811 AttrKey(id->value(), variable.value()))
812 : elemental_.GetAttr(DoubleAttr1::kObjLinCoef,
813 AttrKey(variable.value()));
815
817 const ObjectiveId id, const VariableId first_variable,
818 const VariableId second_variable) const {
819 CHECK(!id.has_value()) << "multiple objectives not supported";
820 return elemental_.GetAttr(
821 SymmetricDoubleAttr2::kObjQuadCoef,
823 second_variable.value()));
824}
825
827 const ObjectiveId id, const VariableId variable) const {
828 return id.has_value()
829 ? elemental_.AttrIsNonDefault(
830 DoubleAttr2::kAuxObjLinCoef,
831 AttrKey(id->value(), variable.value()))
832 : elemental_.AttrIsNonDefault(DoubleAttr1::kObjLinCoef,
833 AttrKey(variable.value()));
834}
835
837 const ObjectiveId id, const VariableId first_variable,
838 const VariableId second_variable) const {
839 CHECK(!id.has_value()) << "multiple objectives not supported";
840 return elemental_.AttrIsNonDefault(
841 SymmetricDoubleAttr2::kObjQuadCoef,
843 second_variable.value()));
844}
845
846absl::string_view ModelStorageV2::objective_name(const ObjectiveId id) const {
847 if (!id.has_value()) return elemental_.primary_objective_name();
848 const auto name =
849 elemental_.GetElementName(AuxiliaryObjectiveId(id->value()));
850 CHECK_OK(name);
851 return *name;
853
855 const bool is_maximize) {
856 if (id.has_value()) {
857 elemental_.SetAttr(BoolAttr1::kAuxObjMaximize, AttrKey(id->value()),
859 } else {
860 elemental_.SetAttr(BoolAttr0::kMaximize, AttrKey(), is_maximize);
861 }
863
865 set_is_maximize(id, true);
866}
867
869 set_is_maximize(id, false);
870}
871
873 const int64_t value) {
874 if (id.has_value()) {
875 elemental_.SetAttr(IntAttr1::kAuxObjPriority, AttrKey(id->value()), value);
876 } else {
877 elemental_.SetAttr(IntAttr0::kObjPriority, AttrKey(), value);
878 }
879}
882 const double value) {
883 if (id.has_value()) {
884 elemental_.SetAttr(DoubleAttr1::kAuxObjOffset, AttrKey(id->value()), value);
885 } else {
886 elemental_.SetAttr(DoubleAttr0::kObjOffset, AttrKey(), value);
887 }
888}
889
891 const VariableId variable,
892 const double value) {
893 if (id.has_value()) {
894 elemental_.SetAttr(DoubleAttr2::kAuxObjLinCoef,
895 AttrKey(id->value(), variable.value()), value);
896 } else {
897 elemental_.SetAttr(DoubleAttr1::kObjLinCoef, AttrKey(variable.value()),
898 value);
899 }
900}
901
903 const ObjectiveId id, const VariableId first_variable,
904 const VariableId second_variable, const double value) {
905 CHECK(!id.has_value()) << "multiple objectives not supported";
906 elemental_.SetAttr(SymmetricDoubleAttr2::kObjQuadCoef,
907 AttrKeyFor<SymmetricDoubleAttr2>(first_variable.value(),
908 second_variable.value()),
909 value);
910}
911
913 if (id.has_value()) {
914 // TODO(b/372645273): Consider adding a `ResetAttr()` method.
915 elemental_.SetAttr(IntAttr1::kAuxObjPriority, AttrKey(id->value()),
917 elemental_.SetAttr(DoubleAttr1::kAuxObjOffset, AttrKey(id->value()),
919 // TODO(b/372645273): Consider adding a `ClearSlice()` method.
920 for (const auto key :
921 elemental_.Slice<0>(DoubleAttr2::kAuxObjLinCoef, id->value())) {
922 elemental_.SetAttr(DoubleAttr2::kAuxObjLinCoef, key,
924 }
925 } else {
926 elemental_.AttrClear(IntAttr0::kObjPriority);
927 elemental_.AttrClear(DoubleAttr0::kObjOffset);
928 elemental_.AttrClear(DoubleAttr1::kObjLinCoef);
929 }
930}
931
932const absl::flat_hash_map<VariableId, double>& ModelStorageV2::linear_objective(
933 const ObjectiveId id) const {
934 LOG(FATAL) << "cannot be implemented";
935}
936
938 if (id.has_value()) {
939 return elemental_.GetSliceSize<0>(DoubleAttr2::kAuxObjLinCoef, id->value());
940 } else {
941 return elemental_.AttrNumNonDefaults(DoubleAttr1::kObjLinCoef);
942 }
943}
944
946 const ObjectiveId id) const {
947 CHECK(!id.has_value()) << "multiple objectives not supported";
948 return elemental_.AttrNumNonDefaults(SymmetricDoubleAttr2::kObjQuadCoef);
949}
950
951std::vector<std::tuple<VariableId, VariableId, double>>
953 CHECK(!id.has_value()) << "multiple objectives not supported";
954 std::vector<std::tuple<VariableId, VariableId, double>> result;
955 result.reserve(
956 elemental_.AttrNumNonDefaults(SymmetricDoubleAttr2::kObjQuadCoef));
957 for (const auto attr_key :
958 elemental_.AttrNonDefaults(SymmetricDoubleAttr2::kObjQuadCoef)) {
959 result.push_back(
960 {VariableId(attr_key[0]), VariableId(attr_key[1]),
961 elemental_.GetAttr(SymmetricDoubleAttr2::kObjQuadCoef, attr_key)});
962 }
963 return result;
964}
965
967// Auxiliary objectives
969
971 const int64_t priority, const absl::string_view name) {
972 const auto id = elemental_.AddElement<ElementType::kAuxiliaryObjective>(name);
973 elemental_.SetAttr(IntAttr1::kAuxObjPriority, AttrKey(id), priority);
974 return id;
975}
976
978 CHECK(elemental_.DeleteElement(id)) << "no auxiliary objective " << id;
979}
980
982 return static_cast<int>(
984}
985
989}
990
992 const AuxiliaryObjectiveId id) {
993 elemental_.EnsureNextElementIdAtLeast(id);
994}
995
997 const AuxiliaryObjectiveId id) const {
998 return elemental_.ElementExists(id);
999}
1000
1001std::vector<AuxiliaryObjectiveId> ModelStorageV2::AuxiliaryObjectives() const {
1002 const auto ids = elemental_.AllElements<ElementType::kAuxiliaryObjective>();
1003 std::vector<AuxiliaryObjectiveId> result;
1004 result.reserve(ids.size());
1005 for (const auto id : ids) {
1006 result.emplace_back(id);
1007 }
1008 return result;
1010
1011std::vector<AuxiliaryObjectiveId> ModelStorageV2::SortedAuxiliaryObjectives()
1012 const {
1013 auto ids = AuxiliaryObjectives();
1014 absl::c_sort(ids);
1015 return ids;
1016}
1017
1018
1019// Atomic constraint template inline implementations.
1021
1022template <typename ConstraintData>
1023typename ConstraintData::IdType ModelStorageV2::AddAtomicConstraint(
1024 const ConstraintData data) {
1025 if constexpr (!ConstraintData::kSupportsElemental) {
1026 LOG(FATAL) << "elemental not supported yet";
1027 } else {
1028 return internal::AddAtomicConstraint(data, elemental_);
1030}
1031
1032template <typename IdType>
1033void ModelStorageV2::DeleteAtomicConstraint(const IdType id) {
1034 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1035 LOG(FATAL) << "elemental not supported yet";
1036 } else {
1037 CHECK(elemental_.DeleteElement(id))
1038 << "no constraint in the model with id: " << id;
1040}
1041
1042template <typename IdType>
1044ModelStorageV2::GetConstraintData(IdType id) const {
1045 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1046 LOG(FATAL) << "elemental not supported yet";
1047 } else {
1048 return internal::GetAtomicConstraint(id, elemental_);
1049 }
1050}
1051
1052template <typename IdType>
1054ModelStorageV2::constraint_data(const IdType) const {
1055 LOG(FATAL) << "not implementable for ModelStorageV2";
1056}
1057
1058template <typename IdType>
1059int64_t ModelStorageV2::num_constraints() const {
1061 LOG(FATAL) << "elemental not supported yet";
1062 } else {
1063 return elemental_.NumElements(AtomicConstraintTraits<IdType>::kElementType);
1064 }
1065}
1066
1067template <typename IdType>
1069 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1070 LOG(FATAL) << "elemental not supported yet";
1071 } else {
1072 return IdType{
1073 elemental_.NextElementId(AtomicConstraintTraits<IdType>::kElementType)};
1075}
1076
1077template <typename IdType>
1079 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1080 LOG(FATAL) << "elemental not supported yet";
1081 } else {
1082 elemental_.EnsureNextElementIdAtLeast(id);
1083 }
1085
1086template <typename IdType>
1087bool ModelStorageV2::has_constraint(const IdType id) const {
1089 LOG(FATAL) << "elemental not supported yet";
1090 } else {
1091 return elemental_.ElementExists(id);
1092 }
1093}
1094
1095template <typename IdType>
1096std::vector<IdType> ModelStorageV2::Constraints() const {
1097 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1098 LOG(FATAL) << "elemental not supported yet";
1099 } else {
1101 auto els = elemental_.AllElements<e>();
1102 std::vector<ElementId<e>> result;
1103 result.reserve(els.size());
1104 for (const ElementId<e> el : els) {
1105 result.push_back(el);
1106 }
1107 return result;
1109}
1110
1111template <typename IdType>
1112std::vector<IdType> ModelStorageV2::SortedConstraints() const {
1113 if constexpr (!AtomicConstraintTraits<IdType>::kSupportsElemental) {
1114 LOG(FATAL) << "elemental not supported yet";
1115 } else {
1116 auto result = Constraints<IdType>();
1117 absl::c_sort(result);
1118 return result;
1119 }
1120}
1121
1122template <typename IdType>
1123std::vector<IdType> ModelStorageV2::ConstraintsWithVariable(
1124 const VariableId) const {
1125 LOG(FATAL) << "not implementable for ModelStorageV2";
1127
1128template <typename IdType>
1129std::vector<VariableId> ModelStorageV2::VariablesInConstraint(
1130 const IdType) const {
1131 LOG(FATAL) << "not implementable for ModelStorageV2";
1132}
1133
1134} // namespace operations_research::math_opt
1135
1136#endif // ORTOOLS_MATH_OPT_STORAGE_MODEL_STORAGE_V2_H_
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
Definition elemental.h:221
const std::string & primary_objective_name() const
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
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)
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
static absl::StatusOr< absl_nonnull std::unique_ptr< ModelStorageV2 > > FromModelProto(const ModelProto &model_proto)
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
std::vector< VariableId > VariablesInConstraint(IdType id) const
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
int64_t num_linear_objective_terms(ObjectiveId id) const
const AtomicConstraintTraits< IdType >::ConstraintData & constraint_data(IdType id) const
std::vector< LinearConstraintId > LinearConstraints() const
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
int64_t objective_priority(ObjectiveId id) const
std::vector< VariableId > Variables() const
bool has_linear_constraint(LinearConstraintId id) const
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
double linear_objective_coefficient(ObjectiveId id, VariableId variable) const
ModelStorageV2(absl::string_view model_name="", absl::string_view primary_objective_name="")
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
ModelStorageV2 & operator=(const ModelStorageV2 &)=delete
void set_is_maximize(ObjectiveId id, bool is_maximize)
AuxiliaryObjectiveId next_auxiliary_objective_id() const
absl_nonnull std::unique_ptr< ModelStorageV2 > Clone(std::optional< absl::string_view > new_name=std::nullopt) const
void ensure_next_auxiliary_objective_id_at_least(AuxiliaryObjectiveId id)
double quadratic_objective_coefficient(ObjectiveId id, VariableId first_variable, VariableId second_variable) const
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
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
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
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)
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
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)
AttrKey< AttrTypeDescriptorT< AttrType >::kNumKeyElements, typename AttrTypeDescriptorT< AttrType >::Symmetry > AttrKeyFor
For infeasible and unbounded see Not checked if options check_solutions_if_inf_or_unbounded and the If options first_solution_only is false
Definition matchers.h:467
ElementId< ElementType::kAuxiliaryObjective > AuxiliaryObjectiveId
Definition elements.h:82
ElementId< ElementType::kVariable > VariableId
Definition elements.h:80
AttrKey(Ints... dims) -> AttrKey< sizeof...(Ints), NoSymmetry >
ElementId< ElementType::kLinearConstraint > LinearConstraintId
Definition elements.h:81
TaggedId< element_type > ElementId
Definition elements.h:59
std::optional< AuxiliaryObjectiveId > ObjectiveId
constexpr AttrTypeDescriptorT< decltype(attr)>::ValueType GetAttrDefaultValue()
ABSL_DEPRECATED("Use TerminateForReason(bool, TerminationReasonProto, absl::string_view) " "instead") TerminationProto TerminateForReason(TerminationReasonProto reason
STL namespace.