Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
model_storage.h
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef OR_TOOLS_MATH_OPT_STORAGE_MODEL_STORAGE_H_
15#define OR_TOOLS_MATH_OPT_STORAGE_MODEL_STORAGE_H_
16
17#include <cstdint>
18#include <limits>
19#include <memory>
20#include <optional>
21#include <string>
22#include <tuple>
23#include <vector>
24
25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/log/check.h"
28#include "absl/status/status.h"
29#include "absl/status/statusor.h"
30#include "absl/strings/string_view.h"
31#include "ortools/math_opt/constraints/indicator/storage.h" // IWYU pragma: export
32#include "ortools/math_opt/constraints/quadratic/storage.h" // IWYU pragma: export
34#include "ortools/math_opt/constraints/sos/storage.h" // IWYU pragma: export
35#include "ortools/math_opt/model.pb.h"
36#include "ortools/math_opt/model_update.pb.h"
37#include "ortools/math_opt/sparse_containers.pb.h"
38#include "ortools/math_opt/storage/atomic_constraint_storage.h" // IWYU pragma: export
45
46namespace operations_research {
47namespace math_opt {
48
49// An index based C++ API for building & storing optimization problems.
50//
51// Note that this API should usually not be used by C++ users that should prefer
52// the math_opt/cpp/model.h API.
53//
54// It supports the efficient creation and modification of an optimization model,
55// and the export of ModelProto and ModelUpdateProto protos.
56//
57// All methods run in amortized O(1) (as amortized over calls to that exact
58// function) unless otherwise specified.
59//
60// Models problems of the form:
61// min sum_{j in J} c_j * x_j + d
62// s.t. lb^c_i <= sum_{j in J} A_ij * x_j <= ub^c_i for all i in I,
63// lb^v_j <= x_j <= ub^v_j for all j in J,
64// x_j integer for all j in Z,
65// where above:
66// * I: the set of linear constraints,
67// * J: the set of variables,
68// * Z: a subset of J, the integer variables,
69// * x: the decision variables (indexed by J),
70// * c: the linear objective, one double per variable,
71// * d: the objective offset, a double scalar,
72// * lb^c: the constraint lower bounds, one double per linear constraint,
73// * ub^c: the constraint upper bounds, one double per linear constraint,
74// * lb^v: the variable lower bounds, one double per variable,
75// * ub^v: the variable upper bounds, one double per variable,
76// * A: the linear constraint matrix, a double per variable/constraint pair.
77//
78// The min in the objective can also be changed to a max.
79//
80// A simple example:
81//
82// Model the problem:
83// max 2.0 * x + y
84// s.t. x + y <= 1.5
85// x in {0.0, 1.0}
86// 0 <= y <= 2.5
87//
88// using ::operations_research::math_opt::ModelStorage;
89// using ::operations_research::math_opt::VariableId;
90// using ::operations_research::math_opt::LinearConstraintId;
91// using ::operations_research::math_opt::ModelProto;
92// using ::operations_research::math_opt::ModelProtoUpdate;
93//
94// ModelStorage model("my_model");
95// const VariableId x = model.AddVariable(0.0, 1.0, true, "x");
96// const VariableId y = model.AddVariable(0.0, 2.5, false, "y");
97// const LinearConstraintId c = model.AddLinearConstraint(
98// -std::numeric_limits<double>::infinity, 1.5, "c");
99// model.set_linear_constraint_coefficient(x, c, 1.0);
100// model.set_linear_constraint_coefficient(y, c, 1.0);
101// model.set_linear_objective_coefficient(kPrimaryObjectiveId, x, 2.0);
102// model.set_linear_objective_coefficient(kPrimaryObjectiveId, y, 1.0);
103// model.set_maximize(kPrimaryObjectiveId);
104//
105// Now, export to a proto describing the model:
106//
107// const ModelProto model_proto = model.ExportModel();
108//
109// Modify the problem and get a model update proto:
110//
111// const UpdateTrackerId update_tracker = model.NewUpdateTracker();
112// c.set_upper_bound(2.0);
113// const std::optional<ModelUpdateProto> update_proto =
114// model.ExportModelUpdate(update_tracker);
115// model.AdvanceCheckpoint(update_tracker);
116//
117// Reading and writing model properties:
118//
119// Properties of the model (e.g. variable/constraint bounds) can be written
120// and read in amortized O(1) time. Deleting a variable will take time
121// O(#constraints containing the variable), and likewise deleting a constraint
122// will take time O(#variables in the constraint). The constraint matrix is
123// stored as hash map where the key is a {LinearConstraintId, VariableId}
124// pair and the value is the coefficient. The nonzeros of the matrix are
125// additionally stored by row and by column.
126//
127// Exporting the Model proto:
128//
129// The Model proto is an equivalent representation to ModelStorage. It has a
130// smaller memory footprint and optimized for storage/transport, rather than
131// efficient modification. It is also the format consumed by solvers in this
132// library. The Model proto can be generated by calling
133// ModelStorage::ExportModel().
134//
135// Incrementalism, the ModelUpdate proto, and Checkpoints:
136//
137// To update an existing model as specified by a Model proto, solvers consume a
138// ModelUpdate proto, which describes the changes to a model (e.g. new variables
139// or a change in a variable bound). ModelStorage::NewUpdateTracker() tracks the
140// changes made and produces a ModelUpdate proto describing these changes with
141// the method ModelStorage::ExportModelUpdate(). The changes returned will be
142// the modifications since the previous call to
143// ModelStorage::AdvanceCheckpoint(). Note that, for newly initialized models,
144// before the first checkpoint, there is no additional memory overhead from
145// tracking changes. See
146// g3doc/ortools/math_opt/g3doc/model_building_complexity.md
147// for details.
148//
149// On bad input:
150//
151// Using a bad variable id or constraint id (an id not in the current model,
152// which includes ids that have been deleted) on any method will result in an
153// immediate failure (either a CHECK failure or an exception, which is an
154// implementation detail you should not rely on). We make no attempt to say if a
155// model is invalid (e.g. a variable lower bound is infinite, exceeds an upper
156// bound, or is NaN). The exported models are validated instead, see
157// model_validator.h.
158class ModelStorage {
159 public:
160 // Returns a storage from the input proto. Returns a failure status if the
161 // input proto is invalid.
162 //
163 // Variable/constraint names can be repeated in the input proto but will be
164 // considered invalid when solving.
165 //
166 // See ApplyUpdateProto() for dealing with subsequent updates.
167 static absl::StatusOr<std::unique_ptr<ModelStorage>> FromModelProto(
168 const ModelProto& model_proto);
169
170 // Creates an empty minimization problem.
171 inline explicit ModelStorage(absl::string_view model_name = "",
172 absl::string_view primary_objective_name = "");
173
174 ModelStorage(const ModelStorage&) = delete;
175 ModelStorage& operator=(const ModelStorage&) = delete;
176
177 // Returns a clone of the model, optionally changing model's name.
178 //
179 // The variables and constraints have the same ids. The clone will also not
180 // reused any id of variable/constraint that was deleted in the original.
181 //
182 // Note that the returned model does not have any update tracker.
183 std::unique_ptr<ModelStorage> Clone(
184 std::optional<absl::string_view> new_name = std::nullopt) const;
185
186 inline const std::string& name() const { return name_; }
187
189 // Variables
191
192 // Adds a continuous unbounded variable to the model and returns its id.
193 //
194 // See AddVariable(double, double, bool, absl::string_view) for details.
195 inline VariableId AddVariable(absl::string_view name = "");
196
197 // Adds a variable to the model and returns its id.
198 //
199 // The returned ids begin at zero and increase by one with each call to
200 // AddVariable. Deleted ids are NOT reused. If no variables are deleted,
201 // the ids in the model will be consecutive.
202 VariableId AddVariable(double lower_bound, double upper_bound,
203 bool is_integer, absl::string_view name = "");
204
205 inline double variable_lower_bound(VariableId id) const;
206 inline double variable_upper_bound(VariableId id) const;
207 inline bool is_variable_integer(VariableId id) const;
208 inline const std::string& variable_name(VariableId id) const;
209
210 inline void set_variable_lower_bound(VariableId id, double lower_bound);
211 inline void set_variable_upper_bound(VariableId id, double upper_bound);
212 inline void set_variable_is_integer(VariableId id, bool is_integer);
213 inline void set_variable_as_integer(VariableId id);
214 inline void set_variable_as_continuous(VariableId id);
215
216 // Removes a variable from the model.
217 //
218 // It is an error to use a deleted variable id as input to any subsequent
219 // function calls on the model. Runs in O(#constraints containing the
220 // variable).
221 void DeleteVariable(VariableId id);
222
223 // The number of variables in the model.
224 //
225 // Equal to the number of variables created minus the number of variables
226 // deleted.
227 inline int num_variables() const;
228
229 // The returned id of the next call to AddVariable.
230 //
231 // Equal to the number of variables created.
232 inline VariableId next_variable_id() const;
233
234 // Sets the next variable id to be the maximum of next_variable_id() and id.
235 inline void ensure_next_variable_id_at_least(VariableId id);
236
237 // Returns true if this id has been created and not yet deleted.
238 inline bool has_variable(VariableId id) const;
239
240 // The VariableIds in use (not deleted), order not defined.
241 std::vector<VariableId> variables() const;
242
243 // Returns a sorted vector of all existing (not deleted) variables in the
244 // model.
245 //
246 // Runs in O(n log(n)), where n is the number of variables returned.
247 std::vector<VariableId> SortedVariables() const;
248
250 // Linear Constraints
252
253 // Adds a linear constraint to the model with a lower bound of -inf and an
254 // upper bound of +inf and returns its id.
255 //
256 // See AddLinearConstraint(double, double, absl::string_view) for details.
257 inline LinearConstraintId AddLinearConstraint(absl::string_view name = "");
258
259 // Adds a linear constraint to the model returns its id.
260 //
261 // The returned ids begin at zero and increase by one with each call to
262 // AddLinearConstraint. Deleted ids are NOT reused. If no linear
263 // constraints are deleted, the ids in the model will be consecutive.
264 LinearConstraintId AddLinearConstraint(double lower_bound, double upper_bound,
265 absl::string_view name = "");
266
267 inline double linear_constraint_lower_bound(LinearConstraintId id) const;
268 inline double linear_constraint_upper_bound(LinearConstraintId id) const;
269 inline const std::string& linear_constraint_name(LinearConstraintId id) const;
270
271 inline void set_linear_constraint_lower_bound(LinearConstraintId id,
272 double lower_bound);
273 inline void set_linear_constraint_upper_bound(LinearConstraintId id,
274 double upper_bound);
275
276 // Removes a linear constraint from the model.
277 //
278 // It is an error to use a deleted linear constraint id as input to any
279 // subsequent function calls on the model. Runs in O(#variables in the linear
280 // constraint).
281 void DeleteLinearConstraint(LinearConstraintId id);
282
283 // The number of linear constraints in the model.
284 //
285 // Equal to the number of linear constraints created minus the number of
286 // linear constraints deleted.
287 inline int num_linear_constraints() const;
288
289 // The returned id of the next call to AddLinearConstraint.
290 //
291 // Equal to the number of linear constraints created.
292 inline LinearConstraintId next_linear_constraint_id() const;
293
294 // Sets the next linear constraint id to be the maximum of
295 // next_linear_constraint_id() and id.
296 inline void ensure_next_linear_constraint_id_at_least(LinearConstraintId id);
297
298 // Returns true if this id has been created and not yet deleted.
299 inline bool has_linear_constraint(LinearConstraintId id) const;
300
301 // The LinearConstraintsIds in use (not deleted), order not defined.
302 std::vector<LinearConstraintId> LinearConstraints() const;
303
304 // Returns a sorted vector of all existing (not deleted) linear constraints in
305 // the model.
306 //
307 // Runs in O(n log(n)), where n is the number of linear constraints returned.
308 std::vector<LinearConstraintId> SortedLinearConstraints() const;
309
311 // Linear constraint matrix
313
314 // Returns 0.0 if the entry is not in matrix.
315 inline double linear_constraint_coefficient(LinearConstraintId constraint,
316 VariableId variable) const;
317 inline bool is_linear_constraint_coefficient_nonzero(
318 LinearConstraintId constraint, VariableId variable) const;
319
320 // Setting a value to 0.0 will delete the {constraint, variable} pair from the
321 // underlying sparse matrix representation (and has no effect if the pair is
322 // not present).
323 inline void set_linear_constraint_coefficient(LinearConstraintId constraint,
324 VariableId variable,
325 double value);
326
327 // The {linear constraint, variable, coefficient} tuples with nonzero linear
328 // constraint matrix coefficients.
329 inline std::vector<std::tuple<LinearConstraintId, VariableId, double>>
330 linear_constraint_matrix() const;
331
332 // Returns the variables with nonzero coefficients in a linear constraint.
333 inline std::vector<VariableId> variables_in_linear_constraint(
334 LinearConstraintId constraint) const;
335
336 // Returns the linear constraints with nonzero coefficients on a variable.
337 inline std::vector<LinearConstraintId> linear_constraints_with_variable(
338 VariableId variable) const;
339
341 // Objectives
342 //
343 // The primary ObjectiveId is `PrimaryObjectiveId`. All auxiliary objectives
344 // are referenced by their corresponding `AuxiliaryObjectiveId`.
346
347 inline bool is_maximize(ObjectiveId id) const;
348 inline int64_t objective_priority(ObjectiveId id) const;
349 inline double objective_offset(ObjectiveId id) const;
350 // Returns 0.0 if this variable has no linear objective coefficient.
351 inline double linear_objective_coefficient(ObjectiveId id,
352 VariableId variable) const;
353 // The ordering of the input variables does not matter.
354 inline double quadratic_objective_coefficient(
355 ObjectiveId id, VariableId first_variable,
356 VariableId second_variable) const;
357 inline bool is_linear_objective_coefficient_nonzero(
358 ObjectiveId id, VariableId variable) const;
359 // The ordering of the input variables does not matter.
360 inline bool is_quadratic_objective_coefficient_nonzero(
361 ObjectiveId id, VariableId first_variable,
362 VariableId second_variable) const;
363 inline const std::string& objective_name(ObjectiveId id) const;
364
365 inline void set_is_maximize(ObjectiveId id, bool is_maximize);
366 inline void set_maximize(ObjectiveId id);
367 inline void set_minimize(ObjectiveId id);
368 inline void set_objective_priority(ObjectiveId id, int64_t value);
369 inline void set_objective_offset(ObjectiveId id, double value);
370
371 // Setting a value to 0.0 will delete the variable from the underlying sparse
372 // representation (and has no effect if the variable is not present).
373 inline void set_linear_objective_coefficient(ObjectiveId id,
374 VariableId variable,
375 double value);
376 // Setting a value to 0.0 will delete the variable pair from the underlying
377 // sparse representation (and has no effect if the pair is not present).
378 // The ordering of the input variables does not matter.
379 inline void set_quadratic_objective_coefficient(ObjectiveId id,
380 VariableId first_variable,
381 VariableId second_variable,
382 double value);
383
384 // Equivalent to calling set_linear_objective_coefficient(v, 0.0) for every
385 // variable with nonzero objective coefficient.
386 //
387 // Runs in O(# nonzero linear/quadratic objective terms).
388 inline void clear_objective(ObjectiveId id);
389
390 // The variables with nonzero linear objective coefficients.
391 inline const absl::flat_hash_map<VariableId, double>& linear_objective(
392 ObjectiveId id) const;
393
394 inline int64_t num_linear_objective_terms(ObjectiveId id) const;
395
396 inline int64_t num_quadratic_objective_terms(ObjectiveId id) const;
397
398 // The variable pairs with nonzero quadratic objective coefficients. The keys
399 // are ordered such that .first <= .second. All values are nonempty.
400 //
401 // TODO(b/233630053) do no allocate the result, expose an iterator API.
402 inline std::vector<std::tuple<VariableId, VariableId, double>>
403 quadratic_objective_terms(ObjectiveId id) const;
404
406 // Auxiliary objectives
408
409 // Adds an auxiliary objective to the model and returns its id.
410 //
411 // The returned ids begin at zero and increase by one with each call to
412 // AddAuxiliaryObjective. Deleted ids are NOT reused. If no auxiliary
413 // objectives are deleted, the ids in the model will be consecutive.
414 //
415 // Objectives are minimized by default; you can change the sense via, e.g.,
416 // `set_is_maximize()`.
417 inline AuxiliaryObjectiveId AddAuxiliaryObjective(
418 int64_t priority, absl::string_view name = "");
419
420 // Removes an auxiliary objective from the model.
421 //
422 // It is an error to use a deleted auxiliary objective id as input to any
423 // subsequent function calls on the model. Runs in O(#variables in the
424 // auxiliary objective).
425 inline void DeleteAuxiliaryObjective(AuxiliaryObjectiveId id);
426
427 // The number of auxiliary objectives in the model.
428 //
429 // Equal to the number of auxiliary objectives created minus the number of
430 // auxiliary objectives deleted.
431 inline int num_auxiliary_objectives() const;
432
433 // The returned id of the next call to AddAuxiliaryObjective.
434 //
435 // Equal to the number of auxiliary objectives created.
436 inline AuxiliaryObjectiveId next_auxiliary_objective_id() const;
437
438 // Sets the next auxiliary objective id to be the maximum of
439 // next_auxiliary_objective_id() and id.
440 inline void ensure_next_auxiliary_objective_id_at_least(
441 AuxiliaryObjectiveId id);
442
443 // Returns true if this id has been created and not yet deleted.
444 inline bool has_auxiliary_objective(AuxiliaryObjectiveId id) const;
445
446 // The AuxiliaryObjectiveIds in use (not deleted), order not defined.
447 inline std::vector<AuxiliaryObjectiveId> AuxiliaryObjectives() const;
448
449 // Returns a sorted vector of all existing (not deleted) auxiliary objectives
450 // in the model.
451 //
452 // Runs in O(n log(n)), where n is the number of auxiliary objectives
453 // returned.
454 inline std::vector<AuxiliaryObjectiveId> SortedAuxiliaryObjectives() const;
455
457 // Atomic Constraints
458 //
459 // These methods do not directly require template specializations to add
460 // support for new constraint families; this should be handled automatically
461 // upon adding a specialization for `AtomicConstraintTraits`.
463
464 // Adds an atomic constraint to the model and returns its id.
465 //
466 // The returned ids begin at zero and increase by one with each call to
467 // `AddAtomicConstraint<ConstraintData>`. Deleted ids are NOT reused. Callers
468 // may use `ensure_next_constraint_id_at_least<ConstraintData>` to configure
469 // custom indices.
470 template <typename ConstraintData>
471 inline typename ConstraintData::IdType AddAtomicConstraint(
472 ConstraintData data);
473
474 // Removes an atomic constraint from the model.
475 //
476 // It is an error to use a deleted constraint id as input to any subsequent
477 // function calls on the model. Runs in O(#variables in the constraint).
478 template <typename IdType>
479 inline void DeleteAtomicConstraint(IdType id);
480
481 // Accesses the data object that fully represents a single atomic constraint.
482 template <typename IdType>
483 inline const typename AtomicConstraintTraits<IdType>::ConstraintData&
484 constraint_data(IdType id) const;
485
486 // Returns the number of atomic constraints in the model of the family
487 // corresponding to `ConstraintData`.
488 //
489 // Equal to the number of such constraints created minus the number of such
490 // constraints deleted.
491 template <typename IdType>
492 inline int64_t num_constraints() const;
493
494 // Returns the smallest valid ID for a new atomic constraint of the family
495 // corresponding to `ConstraintData`.
496 template <typename IdType>
497 inline IdType next_constraint_id() const;
498
499 // Sets the next atomic constraint id of the family corresponding to
500 // `ConstraintData` to be the maximum of
501 // `next_constraint_id<ConstraintData>()` and `id`.
502 template <typename IdType>
503 inline void ensure_next_constraint_id_at_least(IdType id);
504
505 // Returns true if this id has been created and not yet deleted.
506 template <typename IdType>
507 inline bool has_constraint(IdType id) const;
508
509 // Returns the constraint IDs in use (not deleted); order is not defined.
510 template <typename IdType>
511 std::vector<IdType> Constraints() const;
512
513 // Returns a sorted vector of all existing (not deleted) atomic constraints
514 // in the model of the family corresponding to `ConstraintData`.
515 //
516 // Runs in O(n log(n)), where n is the number of constraints returned.
517 template <typename IdType>
518 std::vector<IdType> SortedConstraints() const;
519
520 // Returns the constraint in the given family in which the variable appears
521 // structurally (i.e., has a coefficient, possibly zero). Order is not
522 // defined.
523 template <typename IdType>
524 inline std::vector<IdType> ConstraintsWithVariable(
525 VariableId variable_id) const;
526
527 // Returns the variables appearing in the constraint. Order is not defined.
528 template <typename IdType>
529 inline std::vector<VariableId> VariablesInConstraint(IdType id) const;
530
532 // Export
534
535 // Returns a proto representation of the optimization model.
536 //
537 // See FromModelProto() to build a ModelStorage from a proto.
538 ModelProto ExportModel(bool remove_names = false) const;
539
540 // Creates a tracker that can be used to generate a ModelUpdateProto with the
541 // updates that happened since the last checkpoint. The tracker initial
542 // checkpoint corresponds to the current state of the model.
543 //
544 // Thread-safety: this method must not be used while modifying the
545 // ModelStorage. The user is expected to use proper synchronization primitive
546 // to serialize changes to the model and trackers creations. That said
547 // multiple trackers can be created concurrently.
548 //
549 // For each update tracker we define a checkpoint that is the starting point
550 // used to compute the ModelUpdateProto.
551 //
552 // Example:
553 // ModelStorage model;
554 // ...
555 // ASSIGN_OR_RETURN(const auto solver,
556 // Solver::New(solver_type, model.ExportModel(),
557 // /*initializer=*/{}));
558 // const UpdateTrackerId update_tracker = model.NewUpdatesTracker();
559 //
560 // ASSIGN_OR_RETURN(const auto result_1,
561 // solver->Solve(/*parameters=*/{});
562 //
563 // model.AddVariable(0.0, 1.0, true, "y");
564 // model.set_maximize(true);
565 //
566 // const std::optional<ModelUpdateProto> update_proto =
567 // model.ExportModelUpdate(update_tracker);
568 // model.AdvanceCheckpoint(update_tracker);
569 //
570 // if (update_proto) {
571 // ASSIGN_OR_RETURN(const bool updated, solver->Update(*update_proto));
572 // if (!updated) {
573 // // The update is not supported by the solver, we create a new one.
574 // ASSIGN_OR_RETURN(const auto new_model_proto, model.ExportModel());
575 // ASSIGN_OR_RETURN(solver,
576 // Solver::New(solver_type, new_model_proto,
577 // /*initializer=*/{}));
578 // }
579 // }
580 // ASSIGN_OR_RETURN(const auto result_2,
581 // solver->Solve(/*parameters=*/{});
582 //
583 UpdateTrackerId NewUpdateTracker();
584
585 // Deletes the input tracker.
586 //
587 // It must not be used anymore after its destruction. It can be deleted once,
588 // trying to delete it a second time or use it will raise an assertion
589 // (CHECK).
590 //
591 // The update trackers are automatically deleted when the ModelStorage is
592 // destroyed. Calling this function is thus only useful for performance
593 // reasons, to ensure the ModelStorage does not keep data for update trackers
594 // that are not needed anymore.
595 //
596 // Thread-safety: this method can be called at any time, even during the
597 // creation of other trackers or during model modification. It must not be
598 // called concurrently with ExportModelUpdate() or AdvanceCheckpoint() though.
599 void DeleteUpdateTracker(UpdateTrackerId update_tracker);
600
601 // Returns a proto representation of the changes to the model since the most
602 // recent checkpoint (i.e. last time AdvanceCheckpoint() was called); nullopt
603 // if the update would have been empty.
604 //
605 // Thread-safety: this method must not be used while modifying the
606 // ModelStorage or after calling DeleteUpdateTracker(). The user is expected
607 // to use proper synchronization primitive to serialize changes to the model
608 // and the use of this method.
609 //
610 // It can be called concurrently for different update trackers though.
611 std::optional<ModelUpdateProto> ExportModelUpdate(
612 UpdateTrackerId update_tracker, bool remove_names = false) const;
613
614 // Uses the current model state as the starting point to calculate the
615 // ModelUpdateProto next time ExportModelUpdate() is called.
616 //
617 // Thread-safety: this method must not be used while modifying the
618 // ModelStorage or after calling DeleteUpdateTracker(). The user is expected
619 // to use proper synchronization primitive to serialize changes to the model
620 // and the use of this method.
621 //
622 // It can be called concurrently for different update trackers though.
623 void AdvanceCheckpoint(UpdateTrackerId update_tracker);
624
625 // Apply the provided update to this model. Returns a failure if the update is
626 // not valid.
627 //
628 // As with FromModelProto(), duplicated names are ignored.
629 //
630 // It takes O(num_variables + num_constraints) extra memory and execution to
631 // apply the update (due to the need to build a ModelSummary). So even a small
632 // update will have some cost.
633 absl::Status ApplyUpdateProto(const ModelUpdateProto& update_proto);
634
635 private:
636 struct UpdateTrackerData {
637 UpdateTrackerData(
638 const VariableStorage& variables, const ObjectiveStorage& objectives,
639 const LinearConstraintStorage& linear_constraints,
640 const AtomicConstraintStorage<QuadraticConstraintData>&
641 quadratic_constraints,
642 const AtomicConstraintStorage<SecondOrderConeConstraintData>
643 soc_constraints,
644 const AtomicConstraintStorage<Sos1ConstraintData>& sos1_constraints,
645 const AtomicConstraintStorage<Sos2ConstraintData>& sos2_constraints,
646 const AtomicConstraintStorage<IndicatorConstraintData>&
647 indicator_constraints)
648 : dirty_variables(variables),
649 dirty_objective(objectives, variables.next_id()),
650 dirty_linear_constraints(linear_constraints,
651 dirty_variables.checkpoint),
652 dirty_quadratic_constraints(quadratic_constraints),
653 dirty_soc_constraints(soc_constraints),
654 dirty_sos1_constraints(sos1_constraints),
655 dirty_sos2_constraints(sos2_constraints),
656 dirty_indicator_constraints(indicator_constraints) {}
657
658 // Returns a proto representation of the changes to the model since the most
659 // recent call to SharedCheckpoint() or nullopt if no changes happened.
660 //
661 // Thread-safety: this method is threadsafe.
662 std::optional<ModelUpdateProto> ExportModelUpdate(
663 const ModelStorage& storage, bool remove_names = false) const;
664
665 // Use the current model state as the starting point to calculate the
666 // ModelUpdateProto next time ExportSharedModelUpdate() is called.
667 void AdvanceCheckpoint(const ModelStorage& storage);
668
669 // Implementers of new constraint types should provide a specialization that
670 // returns the address of the appropriate `UpdateTrackerData` field.
671 template <typename ConstraintData>
672 static constexpr typename AtomicConstraintStorage<ConstraintData>::Diff
673 UpdateTrackerData::*
674 AtomicConstraintDirtyFieldPtr();
675
676 // Update information
677 //
678 // Implicitly, all data for variables and constraints added after the last
679 // checkpoint are considered "new" and will NOT be stored in the "dirty"
680 // data structures below.
681
682 VariableStorage::Diff dirty_variables;
683 ObjectiveStorage::Diff dirty_objective;
684 LinearConstraintStorage::Diff dirty_linear_constraints;
685 AtomicConstraintStorage<QuadraticConstraintData>::Diff
686 dirty_quadratic_constraints;
687 AtomicConstraintStorage<SecondOrderConeConstraintData>::Diff
688 dirty_soc_constraints;
689 AtomicConstraintStorage<Sos1ConstraintData>::Diff dirty_sos1_constraints;
690 AtomicConstraintStorage<Sos2ConstraintData>::Diff dirty_sos2_constraints;
691 AtomicConstraintStorage<IndicatorConstraintData>::Diff
692 dirty_indicator_constraints;
693 };
694
695 auto UpdateAndGetVariableDiffs() {
696 return MakeUpdateDataFieldRange<&UpdateTrackerData::dirty_variables>(
697 update_trackers_.GetUpdatedTrackers());
698 }
699
700 auto UpdateAndGetObjectiveDiffs() {
701 return MakeUpdateDataFieldRange<&UpdateTrackerData::dirty_objective>(
702 update_trackers_.GetUpdatedTrackers());
703 }
704
705 auto UpdateAndGetLinearConstraintDiffs() {
707 &UpdateTrackerData::dirty_linear_constraints>(
708 update_trackers_.GetUpdatedTrackers());
709 }
710
711 // Ids must be greater or equal to `next_variable_id()`.
712 void AddVariables(const VariablesProto& variables);
713
714 // Ids must be greater or equal to `next_auxiliary_objective_id()`.
715 void AddAuxiliaryObjectives(
716 const google::protobuf::Map<int64_t, ObjectiveProto>& objectives);
717
718 // Ids must be greater or equal to `next_linear_constraint_id()`.
719 void AddLinearConstraints(const LinearConstraintsProto& linear_constraints);
720
721 void UpdateObjective(ObjectiveId id, const ObjectiveUpdatesProto& update);
722
723 // Updates the objective linear coefficients. The coefficients of variables
724 // not in the input are kept as-is.
725 void UpdateLinearObjectiveCoefficients(
726 ObjectiveId id, const SparseDoubleVectorProto& coefficients);
727
728 // Updates the objective quadratic coefficients. The coefficients of the pairs
729 // of variables not in the input are kept as-is.
730 void UpdateQuadraticObjectiveCoefficients(
731 ObjectiveId id, const SparseDoubleMatrixProto& coefficients);
732
733 // Updates the linear constraints' coefficients. The coefficients of
734 // (constraint, variable) pairs not in the input are kept as-is.
735 void UpdateLinearConstraintCoefficients(
736 const SparseDoubleMatrixProto& coefficients);
737
738 // Implementers of new constraint types should provide a specialization that
739 // returns a non-const reference to the appropriate `ModelStorage` field.
740 template <typename ConstraintData>
741 AtomicConstraintStorage<ConstraintData>& constraint_storage();
742
743 // Implementers of new constraint types should provide a specialization that
744 // returns a const reference to the appropriate `ModelStorage` field.
745 template <typename ConstraintData>
746 const AtomicConstraintStorage<ConstraintData>& constraint_storage() const;
747
748 std::string name_;
749
750 VariableStorage variables_;
751 ObjectiveStorage objectives_;
752 LinearConstraintStorage linear_constraints_;
753
754 AtomicConstraintStorage<QuadraticConstraintData> quadratic_constraints_;
755 AtomicConstraintStorage<SecondOrderConeConstraintData> soc_constraints_;
756 AtomicConstraintStorage<Sos1ConstraintData> sos1_constraints_;
757 AtomicConstraintStorage<Sos2ConstraintData> sos2_constraints_;
758 AtomicConstraintStorage<IndicatorConstraintData> indicator_constraints_;
759
760 UpdateTrackers<UpdateTrackerData> update_trackers_;
761};
762
765// Inlined function implementations
768
769ModelStorage::ModelStorage(const absl::string_view model_name,
770 const absl::string_view primary_objective_name)
771 : name_(model_name), objectives_(primary_objective_name) {}
772
774// Variables
776
777VariableId ModelStorage::AddVariable(absl::string_view name) {
778 return AddVariable(-std::numeric_limits<double>::infinity(),
779 std::numeric_limits<double>::infinity(), false, name);
780}
781
782double ModelStorage::variable_lower_bound(const VariableId id) const {
783 return variables_.lower_bound(id);
784}
785
786double ModelStorage::variable_upper_bound(const VariableId id) const {
787 return variables_.upper_bound(id);
788}
789
790bool ModelStorage::is_variable_integer(VariableId id) const {
791 return variables_.is_integer(id);
792}
793
794const std::string& ModelStorage::variable_name(const VariableId id) const {
795 return variables_.name(id);
796}
797
798void ModelStorage::set_variable_lower_bound(const VariableId id,
799 const double lower_bound) {
800 variables_.set_lower_bound(id, lower_bound, UpdateAndGetVariableDiffs());
801}
802
803void ModelStorage::set_variable_upper_bound(const VariableId id,
804 const double upper_bound) {
805 variables_.set_upper_bound(id, upper_bound, UpdateAndGetVariableDiffs());
806}
807
808void ModelStorage::set_variable_is_integer(const VariableId id,
809 const bool is_integer) {
810 variables_.set_integer(id, is_integer, UpdateAndGetVariableDiffs());
811}
812
813void ModelStorage::set_variable_as_integer(VariableId id) {
814 set_variable_is_integer(id, true);
815}
816
817void ModelStorage::set_variable_as_continuous(VariableId id) {
818 set_variable_is_integer(id, false);
819}
820
821int ModelStorage::num_variables() const { return variables_.size(); }
822
823VariableId ModelStorage::next_variable_id() const {
824 return variables_.next_id();
825}
826
827void ModelStorage::ensure_next_variable_id_at_least(const VariableId id) {
828 variables_.ensure_next_id_at_least(id);
829}
830
831bool ModelStorage::has_variable(const VariableId id) const {
832 return variables_.contains(id);
833}
834
836// Linear Constraints
838
839LinearConstraintId ModelStorage::AddLinearConstraint(absl::string_view name) {
840 return AddLinearConstraint(-std::numeric_limits<double>::infinity(),
841 std::numeric_limits<double>::infinity(), name);
842}
843
844double ModelStorage::linear_constraint_lower_bound(
845 const LinearConstraintId id) const {
846 return linear_constraints_.lower_bound(id);
847}
848
849double ModelStorage::linear_constraint_upper_bound(
850 const LinearConstraintId id) const {
851 return linear_constraints_.upper_bound(id);
852}
853
854const std::string& ModelStorage::linear_constraint_name(
855 const LinearConstraintId id) const {
856 return linear_constraints_.name(id);
857}
858
859void ModelStorage::set_linear_constraint_lower_bound(
860 const LinearConstraintId id, const double lower_bound) {
861 linear_constraints_.set_lower_bound(id, lower_bound,
862 UpdateAndGetLinearConstraintDiffs());
863}
864
865void ModelStorage::set_linear_constraint_upper_bound(
866 const LinearConstraintId id, const double upper_bound) {
867 linear_constraints_.set_upper_bound(id, upper_bound,
868 UpdateAndGetLinearConstraintDiffs());
869}
870
871int ModelStorage::num_linear_constraints() const {
872 return linear_constraints_.size();
873}
874
875LinearConstraintId ModelStorage::next_linear_constraint_id() const {
876 return linear_constraints_.next_id();
877}
878
879void ModelStorage::ensure_next_linear_constraint_id_at_least(
880 LinearConstraintId id) {
881 linear_constraints_.ensure_next_id_at_least(id);
882}
883
884bool ModelStorage::has_linear_constraint(const LinearConstraintId id) const {
885 return linear_constraints_.contains(id);
886}
887
889// Linear Constraint Matrix
891
892double ModelStorage::linear_constraint_coefficient(
893 LinearConstraintId constraint, VariableId variable) const {
894 return linear_constraints_.matrix().get(constraint, variable);
895}
896
897bool ModelStorage::is_linear_constraint_coefficient_nonzero(
898 LinearConstraintId constraint, VariableId variable) const {
899 return linear_constraints_.matrix().contains(constraint, variable);
900}
901
902void ModelStorage::set_linear_constraint_coefficient(
903 const LinearConstraintId constraint, const VariableId variable,
904 const double value) {
905 linear_constraints_.set_term(constraint, variable, value,
906 UpdateAndGetLinearConstraintDiffs());
907}
908
909std::vector<std::tuple<LinearConstraintId, VariableId, double>>
910ModelStorage::linear_constraint_matrix() const {
911 return linear_constraints_.matrix().Terms();
912}
913
914std::vector<VariableId> ModelStorage::variables_in_linear_constraint(
915 LinearConstraintId constraint) const {
916 return linear_constraints_.matrix().row(constraint);
917}
918
919std::vector<LinearConstraintId> ModelStorage::linear_constraints_with_variable(
920 VariableId variable) const {
921 return linear_constraints_.matrix().column(variable);
922}
923
925// Objectives
927
928bool ModelStorage::is_maximize(const ObjectiveId id) const {
929 return objectives_.maximize(id);
930}
931
932int64_t ModelStorage::objective_priority(const ObjectiveId id) const {
933 return objectives_.priority(id);
934}
935
936double ModelStorage::objective_offset(const ObjectiveId id) const {
937 return objectives_.offset(id);
938}
939
940double ModelStorage::linear_objective_coefficient(
941 const ObjectiveId id, const VariableId variable) const {
942 return objectives_.linear_term(id, variable);
943}
944
945double ModelStorage::quadratic_objective_coefficient(
946 const ObjectiveId id, const VariableId first_variable,
947 const VariableId second_variable) const {
948 return objectives_.quadratic_term(id, first_variable, second_variable);
949}
950
951bool ModelStorage::is_linear_objective_coefficient_nonzero(
952 const ObjectiveId id, const VariableId variable) const {
953 return objectives_.linear_terms(id).contains(variable);
954}
955
956bool ModelStorage::is_quadratic_objective_coefficient_nonzero(
957 const ObjectiveId id, const VariableId first_variable,
958 const VariableId second_variable) const {
959 return objectives_.quadratic_terms(id).get(first_variable, second_variable) !=
960 0.0;
961}
962
963const std::string& ModelStorage::objective_name(const ObjectiveId id) const {
964 return objectives_.name(id);
965}
966
967void ModelStorage::set_is_maximize(const ObjectiveId id,
968 const bool is_maximize) {
969 objectives_.set_maximize(id, is_maximize, UpdateAndGetObjectiveDiffs());
970}
971
972void ModelStorage::set_maximize(const ObjectiveId id) {
973 set_is_maximize(id, true);
974}
975
976void ModelStorage::set_minimize(const ObjectiveId id) {
977 set_is_maximize(id, false);
978}
979
980void ModelStorage::set_objective_priority(const ObjectiveId id,
981 const int64_t value) {
982 objectives_.set_priority(id, value, UpdateAndGetObjectiveDiffs());
983}
984
985void ModelStorage::set_objective_offset(const ObjectiveId id,
986 const double value) {
987 objectives_.set_offset(id, value, UpdateAndGetObjectiveDiffs());
988}
989
990void ModelStorage::set_linear_objective_coefficient(const ObjectiveId id,
991 const VariableId variable,
992 const double value) {
993 objectives_.set_linear_term(id, variable, value,
994 UpdateAndGetObjectiveDiffs());
995}
996
997void ModelStorage::set_quadratic_objective_coefficient(
998 const ObjectiveId id, const VariableId first_variable,
999 const VariableId second_variable, const double value) {
1000 objectives_.set_quadratic_term(id, first_variable, second_variable, value,
1001 UpdateAndGetObjectiveDiffs());
1002}
1003
1004void ModelStorage::clear_objective(const ObjectiveId id) {
1005 objectives_.Clear(id, UpdateAndGetObjectiveDiffs());
1006}
1007
1008const absl::flat_hash_map<VariableId, double>& ModelStorage::linear_objective(
1009 const ObjectiveId id) const {
1010 return objectives_.linear_terms(id);
1011}
1012
1013int64_t ModelStorage::num_linear_objective_terms(const ObjectiveId id) const {
1014 return objectives_.linear_terms(id).size();
1015}
1016
1017int64_t ModelStorage::num_quadratic_objective_terms(
1018 const ObjectiveId id) const {
1019 return objectives_.quadratic_terms(id).nonzeros();
1020}
1021
1022std::vector<std::tuple<VariableId, VariableId, double>>
1023ModelStorage::quadratic_objective_terms(const ObjectiveId id) const {
1024 return objectives_.quadratic_terms(id).Terms();
1025}
1026
1028// Auxiliary objectives
1030
1031AuxiliaryObjectiveId ModelStorage::AddAuxiliaryObjective(
1032 const int64_t priority, const absl::string_view name) {
1033 return objectives_.AddAuxiliaryObjective(priority, name);
1034}
1035
1036void ModelStorage::DeleteAuxiliaryObjective(const AuxiliaryObjectiveId id) {
1037 objectives_.Delete(id, UpdateAndGetObjectiveDiffs());
1038}
1039
1040int ModelStorage::num_auxiliary_objectives() const {
1041 return static_cast<int>(objectives_.num_auxiliary_objectives());
1042}
1043
1044AuxiliaryObjectiveId ModelStorage::next_auxiliary_objective_id() const {
1045 return objectives_.next_id();
1046}
1047
1048void ModelStorage::ensure_next_auxiliary_objective_id_at_least(
1049 const AuxiliaryObjectiveId id) {
1050 objectives_.ensure_next_id_at_least(id);
1051}
1052
1053bool ModelStorage::has_auxiliary_objective(
1054 const AuxiliaryObjectiveId id) const {
1055 return objectives_.contains(id);
1056}
1057
1058std::vector<AuxiliaryObjectiveId> ModelStorage::AuxiliaryObjectives() const {
1059 return objectives_.AuxiliaryObjectives();
1060}
1061
1062std::vector<AuxiliaryObjectiveId> ModelStorage::SortedAuxiliaryObjectives()
1063 const {
1064 return objectives_.SortedAuxiliaryObjectives();
1065}
1066
1068// Atomic constraint template inline implementations.
1070
1071template <typename ConstraintData>
1072typename ConstraintData::IdType ModelStorage::AddAtomicConstraint(
1073 ConstraintData data) {
1074 return constraint_storage<ConstraintData>().AddConstraint(data);
1075}
1076
1077template <typename IdType>
1078void ModelStorage::DeleteAtomicConstraint(const IdType id) {
1079 using ConstraintData =
1080 typename AtomicConstraintTraits<IdType>::ConstraintData;
1081 auto& storage = constraint_storage<ConstraintData>();
1082 CHECK(storage.contains(id));
1083 storage.Delete(
1084 id,
1085 MakeUpdateDataFieldRange<
1086 UpdateTrackerData::AtomicConstraintDirtyFieldPtr<ConstraintData>()>(
1087 update_trackers_.GetUpdatedTrackers()));
1088}
1089
1090template <typename IdType>
1091const typename AtomicConstraintTraits<IdType>::ConstraintData&
1092ModelStorage::constraint_data(const IdType id) const {
1093 using ConstraintData =
1094 typename AtomicConstraintTraits<IdType>::ConstraintData;
1095 return constraint_storage<ConstraintData>().data(id);
1096}
1097
1098template <typename IdType>
1099int64_t ModelStorage::num_constraints() const {
1100 using ConstraintData =
1101 typename AtomicConstraintTraits<IdType>::ConstraintData;
1102 return constraint_storage<ConstraintData>().size();
1103}
1104
1105template <typename IdType>
1106IdType ModelStorage::next_constraint_id() const {
1107 using ConstraintData =
1108 typename AtomicConstraintTraits<IdType>::ConstraintData;
1109 return constraint_storage<ConstraintData>().next_id();
1110}
1111
1112template <typename IdType>
1113void ModelStorage::ensure_next_constraint_id_at_least(const IdType id) {
1114 using ConstraintData =
1115 typename AtomicConstraintTraits<IdType>::ConstraintData;
1116 return constraint_storage<ConstraintData>().ensure_next_id_at_least(id);
1117}
1118
1119template <typename IdType>
1120bool ModelStorage::has_constraint(const IdType id) const {
1121 using ConstraintData =
1122 typename AtomicConstraintTraits<IdType>::ConstraintData;
1123 return constraint_storage<ConstraintData>().contains(id);
1124}
1125
1126template <typename IdType>
1127std::vector<IdType> ModelStorage::Constraints() const {
1128 using ConstraintData =
1129 typename AtomicConstraintTraits<IdType>::ConstraintData;
1130 return constraint_storage<ConstraintData>().Constraints();
1131}
1132
1133template <typename IdType>
1134std::vector<IdType> ModelStorage::SortedConstraints() const {
1135 using ConstraintData =
1136 typename AtomicConstraintTraits<IdType>::ConstraintData;
1137 return constraint_storage<ConstraintData>().SortedConstraints();
1138}
1139
1140template <typename IdType>
1141std::vector<IdType> ModelStorage::ConstraintsWithVariable(
1142 const VariableId variable_id) const {
1143 using ConstraintData =
1144 typename AtomicConstraintTraits<IdType>::ConstraintData;
1145 const absl::flat_hash_set<IdType> constraints =
1146 constraint_storage<ConstraintData>().RelatedConstraints(variable_id);
1147 return {constraints.begin(), constraints.end()};
1148}
1149
1150template <typename IdType>
1151std::vector<VariableId> ModelStorage::VariablesInConstraint(
1152 const IdType id) const {
1153 return constraint_data(id).RelatedVariables();
1154}
1155
1157// Atomic constraint template specializations.
1159
1160// --------------------------- Quadratic constraints ---------------------------
1161
1162template <>
1163inline AtomicConstraintStorage<QuadraticConstraintData>&
1164ModelStorage::constraint_storage() {
1165 return quadratic_constraints_;
1166}
1167
1168template <>
1169inline const AtomicConstraintStorage<QuadraticConstraintData>&
1170ModelStorage::constraint_storage() const {
1171 return quadratic_constraints_;
1172}
1173
1174template <>
1175constexpr typename AtomicConstraintStorage<QuadraticConstraintData>::Diff
1176 ModelStorage::UpdateTrackerData::*
1177 ModelStorage::UpdateTrackerData::AtomicConstraintDirtyFieldPtr<
1178 QuadraticConstraintData>() {
1179 return &UpdateTrackerData::dirty_quadratic_constraints;
1180}
1181
1182// ----------------------- Second-order cone constraints -----------------------
1183
1184template <>
1185inline AtomicConstraintStorage<SecondOrderConeConstraintData>&
1186ModelStorage::constraint_storage() {
1187 return soc_constraints_;
1188}
1189
1190template <>
1191inline const AtomicConstraintStorage<SecondOrderConeConstraintData>&
1192ModelStorage::constraint_storage() const {
1193 return soc_constraints_;
1194}
1195
1196template <>
1197constexpr typename AtomicConstraintStorage<SecondOrderConeConstraintData>::Diff
1198 ModelStorage::UpdateTrackerData::*
1199 ModelStorage::UpdateTrackerData::AtomicConstraintDirtyFieldPtr<
1200 SecondOrderConeConstraintData>() {
1201 return &UpdateTrackerData::dirty_soc_constraints;
1202}
1203
1204// ----------------------------- SOS1 constraints ------------------------------
1205
1206template <>
1207inline AtomicConstraintStorage<Sos1ConstraintData>&
1208ModelStorage::constraint_storage() {
1209 return sos1_constraints_;
1210}
1211
1212template <>
1213inline const AtomicConstraintStorage<Sos1ConstraintData>&
1214ModelStorage::constraint_storage() const {
1215 return sos1_constraints_;
1216}
1217
1218template <>
1219constexpr typename AtomicConstraintStorage<Sos1ConstraintData>::Diff
1220 ModelStorage::UpdateTrackerData::*
1221 ModelStorage::UpdateTrackerData::AtomicConstraintDirtyFieldPtr<
1223 return &UpdateTrackerData::dirty_sos1_constraints;
1224}
1225
1226// ----------------------------- SOS2 constraints ------------------------------
1227
1228template <>
1229inline AtomicConstraintStorage<Sos2ConstraintData>&
1230ModelStorage::constraint_storage() {
1231 return sos2_constraints_;
1232}
1233
1234template <>
1235inline const AtomicConstraintStorage<Sos2ConstraintData>&
1236ModelStorage::constraint_storage() const {
1237 return sos2_constraints_;
1238}
1239
1240template <>
1241constexpr typename AtomicConstraintStorage<Sos2ConstraintData>::Diff
1242 ModelStorage::UpdateTrackerData::*
1243 ModelStorage::UpdateTrackerData::AtomicConstraintDirtyFieldPtr<
1245 return &UpdateTrackerData::dirty_sos2_constraints;
1246}
1247
1248// --------------------------- Indicator constraints ---------------------------
1249
1250template <>
1251inline AtomicConstraintStorage<IndicatorConstraintData>&
1252ModelStorage::constraint_storage() {
1253 return indicator_constraints_;
1254}
1255
1256template <>
1257inline const AtomicConstraintStorage<IndicatorConstraintData>&
1258ModelStorage::constraint_storage() const {
1259 return indicator_constraints_;
1260}
1261
1262template <>
1263constexpr typename AtomicConstraintStorage<IndicatorConstraintData>::Diff
1264 ModelStorage::UpdateTrackerData::*
1265 ModelStorage::UpdateTrackerData::AtomicConstraintDirtyFieldPtr<
1266 IndicatorConstraintData>() {
1267 return &UpdateTrackerData::dirty_indicator_constraints;
1268}
1269
1270} // namespace math_opt
1271} // namespace operations_research
1272
1273#endif // OR_TOOLS_MATH_OPT_STORAGE_MODEL_STORAGE_H_
const std::string name
A name for logging purposes.
int64_t value
double upper_bound
double lower_bound
absl::Span< const double > coefficients
internal::SosConstraintData< Sos1ConstraintId > Sos1ConstraintData
Definition storage.h:98
auto MakeUpdateDataFieldRange(const UpdateTrackers &trackers)
Definition iterators.h:121
internal::SosConstraintData< Sos2ConstraintId > Sos2ConstraintData
Definition storage.h:99
In SWIG mode, we don't want anything besides these top-level includes.