OR-Tools  9.3
model_storage.h
Go to the documentation of this file.
1// Copyright 2010-2021 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_CORE_MODEL_STORAGE_H_
15#define OR_TOOLS_MATH_OPT_CORE_MODEL_STORAGE_H_
16
17#include <cstdint>
18#include <limits>
19#include <memory>
20#include <optional>
21#include <string>
22#include <utility>
23#include <vector>
24
25#include "absl/base/thread_annotations.h"
26#include "absl/container/flat_hash_map.h"
27#include "absl/container/flat_hash_set.h"
28#include "absl/meta/type_traits.h"
29#include "absl/status/statusor.h"
30#include "absl/strings/string_view.h"
31#include "absl/synchronization/mutex.h"
34#include "ortools/math_opt/model.pb.h"
35#include "ortools/math_opt/model_update.pb.h"
36
37namespace operations_research {
38namespace math_opt {
39
40DEFINE_STRONG_INT_TYPE(VariableId, int64_t);
41DEFINE_STRONG_INT_TYPE(LinearConstraintId, int64_t);
42DEFINE_STRONG_INT_TYPE(UpdateTrackerId, int64_t);
43
44// An index based C++ API for building & storing optimization problems.
45//
46// Note that this API should usually not be used by C++ users that should prefer
47// the math_opt/cpp/model.h API.
48//
49// It supports the efficient creation and modification of an optimization model,
50// and the export of ModelProto and ModelUpdateProto protos.
51//
52// All methods run in amortized O(1) (as amortized over calls to that exact
53// function) unless otherwise specified.
54//
55// Models problems of the form:
56// min sum_{j in J} c_j * x_j + d
57// s.t. lb^c_i <= sum_{j in J} A_ij * x_j <= ub^c_i for all i in I,
58// lb^v_j <= x_j <= ub^v_j for all j in J,
59// x_j integer for all j in Z,
60// where above:
61// * I: the set of linear constraints,
62// * J: the set of variables,
63// * Z: a subset of J, the integer variables,
64// * x: the decision variables (indexed by J),
65// * c: the linear objective, one double per variable,
66// * d: the objective offset, a double scalar,
67// * lb^c: the constraint lower bounds, one double per linear constraint,
68// * ub^c: the constraint upper bounds, one double per linear constraint,
69// * lb^v: the variable lower bounds, one double per variable,
70// * ub^v: the variable upper bounds, one double per variable,
71// * A: the linear constraint matrix, a double per variable/constraint pair.
72//
73// The min in the objective can also be changed to a max.
74//
75// A simple example:
76//
77// Model the problem:
78// max 2.0 * x + y
79// s.t. x + y <= 1.5
80// x in {0.0, 1.0}
81// 0 <= y <= 2.5
82//
83// using ::operations_research::math_opt::ModelStorage;
84// using ::operations_research::math_opt::VariableId;
85// using ::operations_research::math_opt::LinearConstraintId;
86// using ::operations_research::math_opt::ModelProto;
87// using ::operations_research::math_opt::ModelProtoUpdate;
88//
89// ModelStorage model("my_model");
90// const VariableId x = model.AddVariable(0.0, 1.0, true, "x");
91// const VariableId y = model.AddVariable(0.0, 2.5, false, "y");
92// const LinearConstraintId c = model.AddLinearConstraint(
93// -std::numeric_limits<double>::infinity, 1.5, "c");
94// model.set_linear_constraint_coefficient(x, c, 1.0);
95// model.set_linear_constraint_coefficient(y, c, 1.0);
96// model.set_linear_objective_coefficient(x, 2.0);
97// model.set_linear_objective_coefficient(y, 1.0);
98// model.set_maximize();
99//
100// Now, export to a proto describing the model:
101//
102// const ModelProto model_proto = model.ExportModel();
103//
104// Modify the problem and get a model update proto:
105//
106// const UpdateTrackerId update_tracker = model.NewUpdateTracker();
107// c.set_upper_bound(2.0);
108// const std::optional<ModelUpdateProto> update_proto =
109// model.ExportModelUpdate(update_tracker);
110// model.Checkpoint(update_tracker);
111//
112// Reading and writing model properties:
113//
114// Properties of the model (e.g. variable/constraint bounds) can be written
115// and read in amortized O(1) time. Deleting a variable will take time
116// O(#constraints containing the variable), and likewise deleting a constraint
117// will take time O(#variables in the constraint). The constraint matrix is
118// stored as hash map where the key is a {LinearConstraintId, VariableId}
119// pair and the value is the coefficient. The nonzeros of the matrix are
120// additionally stored by row and by column, but these indices generated lazily
121// upon first use. Asking for the set of variables in a constraint, the
122// constraints in a variable, deleting a variable or constraint, or requesting a
123// ModelUpdate proto will all trigger these additional indices to be generated.
124//
125// Exporting the Model proto:
126//
127// The Model proto is an equivalent representation to ModelStorage. It has a
128// smaller memory footprint and optimized for storage/transport, rather than
129// efficient modification. It is also the format consumed by solvers in this
130// library. The Model proto can be generated by calling
131// ModelStorage::ExportModel().
132//
133// Incrementalism, the ModelUpdate proto, and Checkpoints:
134//
135// To update an existing model as specified by a Model proto, solvers consume a
136// ModelUpdate proto, which describes the changes to a model (e.g. new variables
137// or a change in a variable bound). ModelStorage::NewUpdateTracker() tracks the
138// changes made and produces a ModelUpdate proto describing these changes with
139// the method ModelStorage::ExportModelUpdate(). The changes returned will be
140// the modifications since the previous call to ModelStorage::Checkpoint(). Note
141// that, for newly initialized models, before the first checkpoint, there is no
142// additional memory overhead from tracking changes. See
143// docs/ortools/math_opt/docs/model_building_complexity.md
144// for details.
145//
146// On bad input:
147//
148// Using a bad variable id or constraint id (an id not in the current model,
149// which includes ids that have been deleted) on any method will result in an
150// immediate failure (either a CHECK failure or an exception, which is an
151// implementation detail you should not rely on). We make no attempt to say if a
152// model is invalid (e.g. a variable lower bound is infinite, exceeds an upper
153// bound, or is NaN). The exported models are validated instead, see
154// model_validator.h.
156 public:
157 // Returns a storage from the input proto. Returns a failure status if the
158 // input proto is invalid.
159 //
160 // Variable/constraint names can be repeated in the input proto but will be
161 // considered invalid when solving.
162 //
163 // See ApplyUpdateProto() for dealing with subsequent updates.
164 static absl::StatusOr<std::unique_ptr<ModelStorage>> FromModelProto(
165 const ModelProto& model_proto);
166
167 // Creates an empty minimization problem.
168 explicit ModelStorage(absl::string_view name = "") : name_(name) {}
169
170 ModelStorage(const ModelStorage&) = delete;
172
173 // Returns a clone of the model.
174 //
175 // The variables and constraints have the same ids. The clone will also not
176 // reused any id of variable/constraint that was deleted in the original.
177 //
178 // Note that the returned model does not have any update tracker.
179 std::unique_ptr<ModelStorage> Clone() const;
180
181 inline const std::string& name() const { return name_; }
182
184 // Variables
186
187 // Adds a continuous unbounded variable to the model and returns its id.
188 //
189 // See AddVariable(double, double, bool, absl::string_view) for details.
190 inline VariableId AddVariable(absl::string_view name = "");
191
192 // Adds a variable to the model and returns its id.
193 //
194 // The returned ids begin at zero and increase by one with each call to
195 // AddVariable. Deleted ids are NOT reused. If no variables are deleted,
196 // the ids in the model will be consecutive.
197 VariableId AddVariable(double lower_bound, double upper_bound,
198 bool is_integer, absl::string_view name = "");
199
200 inline double variable_lower_bound(VariableId id) const;
201 inline double variable_upper_bound(VariableId id) const;
202 inline bool is_variable_integer(VariableId id) const;
203 inline const std::string& variable_name(VariableId id) const;
204
205 inline void set_variable_lower_bound(VariableId id, double lower_bound);
206 inline void set_variable_upper_bound(VariableId id, double upper_bound);
207 inline void set_variable_is_integer(VariableId id, bool is_integer);
208 inline void set_variable_as_integer(VariableId id);
209 inline void set_variable_as_continuous(VariableId id);
210
211 // Removes a variable from the model.
212 //
213 // It is an error to use a deleted variable id as input to any subsequent
214 // function calls on the model. Runs in O(#constraints containing the
215 // variable).
216 void DeleteVariable(VariableId id);
217
218 // The number of variables in the model.
219 //
220 // Equal to the number of variables created minus the number of variables
221 // deleted.
222 inline int num_variables() const;
223
224 // The returned id of the next call to AddVariable.
225 //
226 // Equal to the number of variables created.
227 inline VariableId next_variable_id() const;
228
229 // Returns true if this id has been created and not yet deleted.
230 inline bool has_variable(VariableId id) const;
231
232 // The VariableIds in use (not deleted), order not defined.
233 std::vector<VariableId> variables() const;
234
235 // Returns a sorted vector of all existing (not deleted) variables in the
236 // model.
237 //
238 // Runs in O(n log(n)), where n is the number of variables returned.
239 std::vector<VariableId> SortedVariables() const;
240
242 // Linear Constraints
244
245 // Adds a linear constraint to the model with a lower bound of -inf and an
246 // upper bound of +inf and returns its id.
247 //
248 // See AddLinearConstraint(double, double, absl::string_view) for details.
249 inline LinearConstraintId AddLinearConstraint(absl::string_view name = "");
250
251 // Adds a linear constraint to the model returns its id.
252 //
253 // The returned ids begin at zero and increase by one with each call to
254 // AddLinearConstraint. Deleted ids are NOT reused. If no linear
255 // constraints are deleted, the ids in the model will be consecutive.
256 LinearConstraintId AddLinearConstraint(double lower_bound, double upper_bound,
257 absl::string_view name = "");
258
259 inline double linear_constraint_lower_bound(LinearConstraintId id) const;
260 inline double linear_constraint_upper_bound(LinearConstraintId id) const;
261 inline const std::string& linear_constraint_name(LinearConstraintId id) const;
262
263 inline void set_linear_constraint_lower_bound(LinearConstraintId id,
264 double lower_bound);
265 inline void set_linear_constraint_upper_bound(LinearConstraintId id,
266 double upper_bound);
267
268 // Removes a linear constraint from the model.
269 //
270 // It is an error to use a deleted linear constraint id as input to any
271 // subsequent function calls on the model. Runs in O(#variables in the linear
272 // constraint).
273 void DeleteLinearConstraint(LinearConstraintId id);
274
275 // The number of linear constraints in the model.
276 //
277 // Equal to the number of linear constraints created minus the number of
278 // linear constraints deleted.
279 inline int num_linear_constraints() const;
280
281 // The returned id of the next call to AddLinearConstraint.
282 //
283 // Equal to the number of linear constraints created.
284 inline LinearConstraintId next_linear_constraint_id() const;
285
286 // Returns true if this id has been created and not yet deleted.
287 inline bool has_linear_constraint(LinearConstraintId id) const;
288
289 // The LinearConstraintsIds in use (not deleted), order not defined.
290 std::vector<LinearConstraintId> linear_constraints() const;
291
292 // Returns a sorted vector of all existing (not deleted) linear constraints in
293 // the model.
294 //
295 // Runs in O(n log(n)), where n is the number of linear constraints returned.
296 std::vector<LinearConstraintId> SortedLinearConstraints() const;
297
299 // Linear constraint matrix
301
302 // Returns 0.0 if the entry is not in matrix.
303 inline double linear_constraint_coefficient(LinearConstraintId constraint,
304 VariableId variable) const;
306 LinearConstraintId constraint, VariableId variable) const;
307
308 // Setting a value to 0.0 will delete the {constraint, variable} pair from the
309 // underlying sparse matrix representation (and has no effect if the pair is
310 // not present).
311 inline void set_linear_constraint_coefficient(LinearConstraintId constraint,
312 VariableId variable,
313 double value);
314
315 // The {linear constraint, variable} pairs with nonzero linear constraint
316 // matrix coefficients.
317 inline const absl::flat_hash_map<std::pair<LinearConstraintId, VariableId>,
318 double>&
320
321 // Returns the variables with nonzero coefficients in a linear constraint.
322 //
323 // Runs in O(1), but triggers allocations that are O(nnz) on first use through
324 // a lazy initialization.
325 inline const absl::flat_hash_set<VariableId>& variables_in_linear_constraint(
326 LinearConstraintId constraint);
327
328 // Returns the linear constraints with nonzero coefficients on a variable.
329 //
330 // Runs in O(1), but triggers allocations that are O(nnz) on first use through
331 // a lazy initialization.
332 inline const absl::flat_hash_set<LinearConstraintId>&
333 linear_constraints_with_variable(VariableId variable);
334
336 // Objective
338
339 inline bool is_maximize() const;
340 inline double objective_offset() const;
341 // Returns 0.0 if this variable has no linear objective coefficient.
342 inline double linear_objective_coefficient(VariableId variable) const;
343 // The ordering of the input variables does not matter.
345 VariableId first_variable, VariableId second_variable) const;
347 VariableId variable) const;
348 // The ordering of the input variables does not matter.
350 VariableId first_variable, VariableId second_variable) const;
351
352 inline void set_is_maximize(bool is_maximize);
353 inline void set_maximize();
354 inline void set_minimize();
355 inline void set_objective_offset(double value);
356
357 // Setting a value to 0.0 will delete the variable from the underlying sparse
358 // representation (and has no effect if the variable is not present).
359 inline void set_linear_objective_coefficient(VariableId variable,
360 double value);
361 // Setting a value to 0.0 will delete the variable pair from the underlying
362 // sparse representation (and has no effect if the pair is not present).
363 // The ordering of the input variables does not matter.
364 inline void set_quadratic_objective_coefficient(VariableId first_variable,
365 VariableId second_variable,
366 double value);
367
368 // Equivalent to calling set_linear_objective_coefficient(v, 0.0) for every
369 // variable with nonzero objective coefficient.
370 //
371 // Runs in O(# nonzero linear/quadratic objective terms).
372 inline void clear_objective();
373
374 // The variables with nonzero linear objective coefficients.
375 inline const absl::flat_hash_map<VariableId, double>& linear_objective()
376 const;
377
378 // The variable pairs with nonzero quadratic objective coefficients. The keys
379 // are ordered such that .first <= .second.
380 inline const absl::flat_hash_map<std::pair<VariableId, VariableId>, double>&
381 quadratic_objective() const;
382
383 // Returns a sorted vector of all variables in the model with nonzero linear
384 // objective coefficients.
385 //
386 // Runs in O(n log(n)), where n is the number of variables returned.
387 std::vector<VariableId> SortedLinearObjectiveNonzeroVariables() const;
388
390 // Export
392
393 // Returns a proto representation of the optimization model.
394 //
395 // See FromModelProto() to build a ModelStorage from a proto.
396 ModelProto ExportModel() const;
397
398 // Creates a tracker that can be used to generate a ModelUpdateProto with the
399 // updates that happened since the last checkpoint. The tracker initial
400 // checkpoint corresponds to the current state of the model.
401 //
402 // Thread-safety: this method must not be used while modifying the
403 // ModelStorage. The user is expected to use proper synchronization primitive
404 // to serialize changes to the model and the use of this method. It can be
405 // called concurrently to create multiple trackers though.
406 //
407 // For each update tracker we define a checkpoint that is the starting point
408 // used to compute the ModelUpdateProto.
409 //
410 // Example:
411 // ModelStorage model;
412 // ...
413 // ASSIGN_OR_RETURN(const auto solver,
414 // Solver::New(solver_type, model.ExportModel(),
415 // /*initializer=*/{}));
416 // const UpdateTrackerId update_tracker = model.NewUpdatesTracker();
417 //
418 // ASSIGN_OR_RETURN(const auto result_1,
419 // solver->Solve(/*parameters=*/{});
420 //
421 // model.AddVariable(0.0, 1.0, true, "y");
422 // model.set_maximize(true);
423 //
424 // const std::optional<ModelUpdateProto> update_proto =
425 // model.ExportModelUpdate(update_tracker);
426 // model.Checkpoint(update_tracker);
427 //
428 // if (update_proto) {
429 // ASSIGN_OR_RETURN(const bool updated, solver->Update(*update_proto));
430 // if (!updated) {
431 // // The update is not supported by the solver, we create a new one.
432 // ASSIGN_OR_RETURN(const auto new_model_proto, model.ExportModel());
433 // ASSIGN_OR_RETURN(solver,
434 // Solver::New(solver_type, new_model_proto,
435 // /*initializer=*/{}));
436 // }
437 // }
438 // ASSIGN_OR_RETURN(const auto result_2,
439 // solver->Solve(/*parameters=*/{});
440 //
441 UpdateTrackerId NewUpdateTracker();
442
443 // Deletes the input tracker.
444 //
445 // It must not be used anymore after its destruction. It can be deleted once,
446 // trying to delete it a second time or use it will raise an assertion
447 // (CHECK).
448 //
449 // The update trackers are automatically deleted when the ModelStorage is
450 // destroyed. Calling this function is thus only useful for performance
451 // reasons, to ensure the ModelStorage does not keep data for update trackers
452 // that are not needed anymore.
453 //
454 // Thread-safety: this method must not be used while modifying the
455 // ModelStorage. The user is expected to use proper synchronization primitive
456 // to serialize changes to the model and the use of this method.
457 //
458 // It can be called concurrently to delete multiple trackers though.
459 void DeleteUpdateTracker(UpdateTrackerId update_tracker);
460
461 // Returns a proto representation of the changes to the model since the most
462 // recent checkpoint (i.e. last time Checkpoint() was called); nullopt if the
463 // update would have been empty.
464 //
465 // Thread-safety: this method must not be used while modifying the
466 // ModelStorage. The user is expected to use proper synchronization
467 // primitive to serialize changes to the model and the use of this method.
468 //
469 // It can be called concurrently for different update trackers though.
470 std::optional<ModelUpdateProto> ExportModelUpdate(
471 UpdateTrackerId update_tracker);
472
473 // Uses the current model state as the starting point to calculate the
474 // ModelUpdateProto next time ExportModelUpdate() is called.
475 //
476 // Thread-safety: this method must not be used while modifying the
477 // ModelStorage. The user is expected to use proper synchronization
478 // primitive to serialize changes to the model and the use of this method.
479 //
480 // It can be called concurrently for different update trackers though.
481 void Checkpoint(UpdateTrackerId update_tracker);
482
483 // Apply the provided update to this model. Returns a failure if the update is
484 // not valid.
485 //
486 // As with FromModelProto(), duplicated names are ignored.
487 //
488 // It takes O(num_variables + num_constraints) extra memory and execution to
489 // apply the update (due to the need to build a ModelSummary). So even a small
490 // update will have some cost.
491 absl::Status ApplyUpdateProto(const ModelUpdateProto& update_proto);
492
493 private:
494 struct VariableData {
495 double lower_bound = -std::numeric_limits<double>::infinity();
496 double upper_bound = std::numeric_limits<double>::infinity();
497 bool is_integer = false;
498 std::string name = "";
499 };
500
501 struct LinearConstraintData {
502 double lower_bound = -std::numeric_limits<double>::infinity();
503 double upper_bound = std::numeric_limits<double>::infinity();
504 std::string name = "";
505 };
506
507 struct UpdateTrackerData {
508 // All incremental updates that occurred since last checkpoint. It is
509 // filled-in each time Checkpoint() is called on any update tracker. When an
510 // ExportModelUpdate() is requested on a tracker, all these are merged along
511 // with the remaining updates.
512 //
513 // Guarded by: ModelStorage::update_trackers_lock_. There does not seem to
514 // be a way to use ABSL_GUARDED_BY here.
515 std::vector<std::shared_ptr<const ModelUpdateProto>> updates;
516 };
517
518 template <typename T>
519 void set_variable_property(VariableId id, T value, T VariableData::*field,
520 absl::flat_hash_set<VariableId>& dirty_set);
521
522 inline void set_linear_constraint_property(
523 const LinearConstraintId id, double value,
524 double LinearConstraintData::*field,
525 absl::flat_hash_set<LinearConstraintId>& dirty_set);
526
527 // Adds a variable at the given id, updating next_variable_id_ and the lazy
528 // collections as side effect. It CHECKs that the provided id is not less than
529 // next_variable_id_.
530 //
531 // This is used internally by AddVariable() and AddVariables().
532 void AddVariableInternal(VariableId id, double lower_bound,
533 double upper_bound, bool is_integer,
534 absl::string_view name);
535
536 // Adds all variables from the given proto using AddVariableInternal(). Thus
537 // Ids must be greater or equal to next_variable_id_.
538 void AddVariables(const VariablesProto& variables);
539
540 // Adds a linear constraint at the given id, updating next_variable_id_ and
541 // the lazy collections as side effect. It CHECKs that the provided id is not
542 // less than next_linear_constraint_id_.
543 //
544 // This is used internally by AddLinearConstraint() and
545 // AddLinearConstraints().
546 void AddLinearConstraintInternal(LinearConstraintId id, double lower_bound,
547 double upper_bound, absl::string_view name);
548
549 // Adds all constraints from the given proto using
550 // AddLinearConstraintInternal(). Thus Ids must be greater or equal to
551 // next_linear_constraint_id_.
552 void AddLinearConstraints(const LinearConstraintsProto& linear_constraints);
553
554 // Updates the objective linear coefficients. The coefficients of variables
555 // not in the input are kept as-is.
556 void UpdateLinearObjectiveCoefficients(
557 const SparseDoubleVectorProto& coefficients);
558
559 // Updates the objective quadratic coefficients. The coefficients of the pairs
560 // of variables not in the input are kept as-is.
561 void UpdateQuadraticObjectiveCoefficients(
562 const SparseDoubleMatrixProto& coefficients);
563
564 // Updates the linear constraints' coefficients. The coefficients of
565 // (constraint, variable) pairs not in the input are kept as-is.
566 void UpdateLinearConstraintCoefficients(
567 const SparseDoubleMatrixProto& coefficients);
568
569 // Initializes lazy_matrix_columns_ (column major storage of the linear
570 // constraint matrix) if it is still empty and there is at least one variable
571 // in the model.
572 void EnsureLazyMatrixColumns();
573
574 // Initializes lazy_matrix_rows_ (row major storage of the linear constraint
575 // matrix) if it is still empty and there is at least one linear constraint in
576 // the model.
577 void EnsureLazyMatrixRows();
578
579 // Initializes lazy_quadratic_objective_by_variable_ if it is still empty and
580 // there is at least one variable in the model.
581 void EnsureLazyQuadraticObjective();
582
583 // Export a single variable to proto.
584 void AppendVariable(VariableId id, VariablesProto& variables_proto) const;
585
586 // Export a single linear constraint to proto.
587 void AppendLinearConstraint(
588 LinearConstraintId id,
589 LinearConstraintsProto& linear_constraints_proto) const;
590
591 // Returns a proto representation of the changes to the model since the most
592 // recent call to SharedCheckpoint() or nullopt if no changes happened.
593 //
594 // Thread-safety: this method must not be called concurrently (due to
595 // EnsureLazyMatrixXxx() functions).
596 std::optional<ModelUpdateProto> ExportSharedModelUpdate()
597 ABSL_EXCLUSIVE_LOCKS_REQUIRED(update_trackers_lock_);
598
599 // Use the current model state as the starting point to calculate the
600 // ModelUpdateProto next time ExportSharedModelUpdate() is called.
601 void SharedCheckpoint() ABSL_EXCLUSIVE_LOCKS_REQUIRED(update_trackers_lock_);
602
603 // Same as Checkpoint() but the caller must have acquired the
604 // update_trackers_lock_ mutex.
605 void CheckpointLocked(UpdateTrackerId update_tracker)
606 ABSL_EXCLUSIVE_LOCKS_REQUIRED(update_trackers_lock_);
607
608 std::string name_;
609 VariableId next_variable_id_ = VariableId(0);
610 LinearConstraintId next_linear_constraint_id_ = LinearConstraintId(0);
611
612 bool is_maximize_ = false;
613 double objective_offset_ = 0.0;
614
615 absl::flat_hash_map<VariableId, VariableData> variables_;
616 absl::flat_hash_map<LinearConstraintId, LinearConstraintData>
617 linear_constraints_;
618 // The values of the map must never include zero.
619 absl::flat_hash_map<VariableId, double> linear_objective_;
620 // The values of the map must never include zero. The keys must be upper
621 // triangular, i.e. .first <= .second.
622 absl::flat_hash_map<std::pair<VariableId, VariableId>, double>
623 quadratic_objective_;
624 // The values of the map must never include zero.
625 absl::flat_hash_map<std::pair<LinearConstraintId, VariableId>, double>
626 linear_constraint_matrix_;
627 absl::flat_hash_map<VariableId, absl::flat_hash_set<LinearConstraintId>>
628 lazy_matrix_columns_;
629 absl::flat_hash_map<LinearConstraintId, absl::flat_hash_set<VariableId>>
630 lazy_matrix_rows_;
631 // To handle deletions we need to have an efficient way to look up which
632 // quadratic objective terms involve a given variable. This map stores this
633 // information where the key corresponds to a variable and the value is the
634 // set of all variables appearing in a quadratic objective term with the key.
635 // This data structure is only initialized after a call to
636 // EnsureLazyQuadraticObjective. As of 11/17/2021, this will have occurred if
637 // a nonzero quadratic objective term has ever been added to the model.
638 absl::flat_hash_map<VariableId, absl::flat_hash_set<VariableId>>
639 lazy_quadratic_objective_by_variable_;
640
641 // Update information
642 //
643 // Implicitly, all data for variables and constraints added after the last
644 // checkpoint are considered "new" and will NOT be stored in the "dirty" data
645 // structures below.
646 VariableId variables_checkpoint_ = VariableId(0);
647 LinearConstraintId linear_constraints_checkpoint_ = LinearConstraintId(0);
648 bool dirty_objective_direction_ = false;
649 bool dirty_objective_offset_ = false;
650
651 absl::flat_hash_set<VariableId> dirty_variable_deletes_;
652 absl::flat_hash_set<VariableId> dirty_variable_lower_bounds_;
653 absl::flat_hash_set<VariableId> dirty_variable_upper_bounds_;
654 absl::flat_hash_set<VariableId> dirty_variable_is_integer_;
655
656 absl::flat_hash_set<VariableId> dirty_linear_objective_coefficients_;
657 // NOTE: quadratic objective coefficients are considered dirty, and therefore
658 // tracked in this set, if and only if both variables in the term are "old",
659 // i.e. not added since the last checkpoint.
660 absl::flat_hash_set<std::pair<VariableId, VariableId>>
661 dirty_quadratic_objective_coefficients_;
662
663 absl::flat_hash_set<LinearConstraintId> dirty_linear_constraint_deletes_;
664 absl::flat_hash_set<LinearConstraintId> dirty_linear_constraint_lower_bounds_;
665 absl::flat_hash_set<LinearConstraintId> dirty_linear_constraint_upper_bounds_;
666
667 // Only for pairs where both the variable and constraint are before the
668 // checkpoint, i.e.
669 // var_id < variables_checkpoint_ &&
670 // lin_con_id < linear_constraints_checkpoint_
671 absl::flat_hash_set<std::pair<LinearConstraintId, VariableId>>
672 dirty_linear_constraint_matrix_keys_;
673
674 // Lock used to serialize access to update_trackers_ and to the all fields of
675 // UpdateTrackerData. We use only one lock since trackers are modified in
676 // group (they share a chain of ModelUpdateProto and the update of one tracker
677 // usually requires the update of some of the others).
678 absl::Mutex update_trackers_lock_;
679
680 // Next index to use in NewUpdateTracker().
681 UpdateTrackerId next_update_tracker_
682 ABSL_GUARDED_BY(update_trackers_lock_) = {};
683
684 // The UpdateTracker instances tracking the changes of to this model.
685 absl::flat_hash_map<UpdateTrackerId, std::unique_ptr<UpdateTrackerData>>
686 update_trackers_ ABSL_GUARDED_BY(update_trackers_lock_);
687};
688
691// Inlined function implementations
694
696// Variables
698
699VariableId ModelStorage::AddVariable(absl::string_view name) {
700 return AddVariable(-std::numeric_limits<double>::infinity(),
701 std::numeric_limits<double>::infinity(), false, name);
702}
703
704double ModelStorage::variable_lower_bound(const VariableId id) const {
705 return variables_.at(id).lower_bound;
706}
707
708double ModelStorage::variable_upper_bound(const VariableId id) const {
709 return variables_.at(id).upper_bound;
710}
711
712bool ModelStorage::is_variable_integer(VariableId id) const {
713 return variables_.at(id).is_integer;
714}
715
716const std::string& ModelStorage::variable_name(const VariableId id) const {
717 return variables_.at(id).name;
718}
719
720template <typename T>
721void ModelStorage::set_variable_property(
722 const VariableId id, const T value, T VariableData::*const field,
723 absl::flat_hash_set<VariableId>& dirty_set) {
724 VariableData& var_data = variables_.at(id);
725 if (var_data.*field != value) {
726 var_data.*field = value;
727 if (id < variables_checkpoint_) {
728 dirty_set.insert(id);
729 }
730 }
731}
732
734 const double lower_bound) {
735 set_variable_property(id, lower_bound, &VariableData::lower_bound,
736 dirty_variable_lower_bounds_);
737}
738
740 const double upper_bound) {
741 set_variable_property(id, upper_bound, &VariableData::upper_bound,
742 dirty_variable_upper_bounds_);
743}
744
746 const bool is_integer) {
747 set_variable_property(id, is_integer, &VariableData::is_integer,
748 dirty_variable_is_integer_);
749}
750
752 set_variable_is_integer(id, true);
753}
754
756 set_variable_is_integer(id, false);
757}
758
759int ModelStorage::num_variables() const { return variables_.size(); }
760
761VariableId ModelStorage::next_variable_id() const { return next_variable_id_; }
762
763bool ModelStorage::has_variable(const VariableId id) const {
764 return variables_.contains(id);
765}
766
768// Linear Constraints
770
771LinearConstraintId ModelStorage::AddLinearConstraint(absl::string_view name) {
772 return AddLinearConstraint(-std::numeric_limits<double>::infinity(),
773 std::numeric_limits<double>::infinity(), name);
774}
775
777 const LinearConstraintId id) const {
778 return linear_constraints_.at(id).lower_bound;
779}
780
782 const LinearConstraintId id) const {
783 return linear_constraints_.at(id).upper_bound;
784}
785
787 const LinearConstraintId id) const {
788 return linear_constraints_.at(id).name;
789}
790
791void ModelStorage::set_linear_constraint_property(
792 const LinearConstraintId id, const double value,
793 double LinearConstraintData::*const field,
794 absl::flat_hash_set<LinearConstraintId>& dirty_set) {
795 LinearConstraintData& lin_con_data = linear_constraints_.at(id);
796 if (lin_con_data.*field != value) {
797 lin_con_data.*field = value;
798 if (id < linear_constraints_checkpoint_) {
799 dirty_set.insert(id);
800 }
801 }
802}
803
805 const LinearConstraintId id, const double lower_bound) {
806 set_linear_constraint_property(id, lower_bound,
808 dirty_linear_constraint_lower_bounds_);
809}
810
812 const LinearConstraintId id, const double upper_bound) {
813 set_linear_constraint_property(id, upper_bound,
815 dirty_linear_constraint_upper_bounds_);
816}
817
819 return linear_constraints_.size();
820}
821
822LinearConstraintId ModelStorage::next_linear_constraint_id() const {
823 return next_linear_constraint_id_;
824}
825
826bool ModelStorage::has_linear_constraint(const LinearConstraintId id) const {
827 return linear_constraints_.contains(id);
828}
829
831// Linear Constraint Matrix
833
835 LinearConstraintId constraint, VariableId variable) const {
836 return gtl::FindWithDefault(linear_constraint_matrix_,
837 {constraint, variable});
838}
839
841 LinearConstraintId constraint, VariableId variable) const {
842 return linear_constraint_matrix_.contains({constraint, variable});
843}
844
846 LinearConstraintId constraint, VariableId variable, double value) {
847 bool was_updated = false;
848 if (value == 0.0) {
849 if (linear_constraint_matrix_.erase({constraint, variable}) > 0) {
850 was_updated = true;
851 if (!lazy_matrix_columns_.empty()) {
852 lazy_matrix_columns_.at(variable).erase(constraint);
853 }
854 if (!lazy_matrix_rows_.empty()) {
855 lazy_matrix_rows_.at(constraint).erase(variable);
856 }
857 }
858 } else {
859 const auto [iterator, inserted] =
860 linear_constraint_matrix_.try_emplace({constraint, variable}, value);
861 if (inserted) {
862 was_updated = true;
863 } else if (iterator->second != value) {
864 iterator->second = value;
865 was_updated = true;
866 }
867 if (!lazy_matrix_columns_.empty()) {
868 lazy_matrix_columns_.at(variable).insert(constraint);
869 }
870 if (!lazy_matrix_rows_.empty()) {
871 lazy_matrix_rows_.at(constraint).insert(variable);
872 }
873 }
874 if (was_updated && constraint < linear_constraints_checkpoint_ &&
875 variable < variables_checkpoint_) {
876 dirty_linear_constraint_matrix_keys_.emplace(constraint, variable);
877 }
878}
879
880const absl::flat_hash_map<std::pair<LinearConstraintId, VariableId>, double>&
882 return linear_constraint_matrix_;
883}
884
885const absl::flat_hash_set<VariableId>&
886ModelStorage::variables_in_linear_constraint(LinearConstraintId constraint) {
887 EnsureLazyMatrixRows();
888 return lazy_matrix_rows_.at(constraint);
889}
890
891const absl::flat_hash_set<LinearConstraintId>&
893 EnsureLazyMatrixColumns();
894 return lazy_matrix_columns_.at(variable);
895}
896
898// Objective
900
901namespace internal {
902
903inline std::pair<VariableId, VariableId> MakeOrderedPair(const VariableId a,
904 const VariableId b) {
905 return a < b ? std::make_pair(a, b) : std::make_pair(b, a);
906}
907
908} // namespace internal
909
910bool ModelStorage::is_maximize() const { return is_maximize_; }
911
912double ModelStorage::objective_offset() const { return objective_offset_; }
913
914double ModelStorage::linear_objective_coefficient(VariableId variable) const {
915 return gtl::FindWithDefault(linear_objective_, variable);
916}
917
919 const VariableId first_variable, const VariableId second_variable) const {
921 quadratic_objective_,
922 internal::MakeOrderedPair(first_variable, second_variable));
923}
924
926 VariableId variable) const {
927 return linear_objective_.contains(variable);
928}
929
931 const VariableId first_variable, const VariableId second_variable) const {
932 return quadratic_objective_.contains(
933 internal::MakeOrderedPair(first_variable, second_variable));
934}
935
936void ModelStorage::set_is_maximize(bool is_maximize) {
937 if (is_maximize_ != is_maximize) {
938 dirty_objective_direction_ = true;
939 is_maximize_ = is_maximize;
940 }
941}
942
944
946
948 if (value != objective_offset_) {
949 dirty_objective_offset_ = true;
950 objective_offset_ = value;
951 }
952}
953
955 double value) {
956 bool was_updated = false;
957 if (value == 0.0) {
958 if (linear_objective_.erase(variable) > 0) {
959 was_updated = true;
960 }
961 } else {
962 const auto [iterator, inserted] =
963 linear_objective_.try_emplace(variable, value);
964 if (inserted) {
965 was_updated = true;
966 } else if (iterator->second != value) {
967 iterator->second = value;
968 was_updated = true;
969 }
970 }
971 if (was_updated && variable < variables_checkpoint_) {
972 dirty_linear_objective_coefficients_.insert(variable);
973 }
974}
975
977 const VariableId first_variable, const VariableId second_variable,
978 double value) {
979 const std::pair<VariableId, VariableId> key =
980 internal::MakeOrderedPair(first_variable, second_variable);
981 bool was_updated = false;
982 if (value == 0.0) {
983 if (quadratic_objective_.erase(key) > 0) {
984 was_updated = true;
985 }
986 } else {
987 const auto [iterator, inserted] =
988 quadratic_objective_.try_emplace(key, value);
989 if (inserted) {
990 was_updated = true;
991 } else if (iterator->second != value) {
992 iterator->second = value;
993 was_updated = true;
994 }
995 }
996 if (was_updated) {
997 if (!lazy_quadratic_objective_by_variable_.empty()) {
998 lazy_quadratic_objective_by_variable_.at(first_variable)
999 .insert(second_variable);
1000 lazy_quadratic_objective_by_variable_.at(second_variable)
1001 .insert(first_variable);
1002 }
1003 if (key.second < variables_checkpoint_) {
1004 dirty_quadratic_objective_coefficients_.insert(key);
1005 }
1006 }
1007}
1008
1011 while (!linear_objective_.empty()) {
1012 set_linear_objective_coefficient(linear_objective_.begin()->first, 0.0);
1013 }
1014 while (!quadratic_objective_.empty()) {
1016 quadratic_objective_.begin()->first.first,
1017 quadratic_objective_.begin()->first.second, 0.0);
1018 }
1019}
1020
1021const absl::flat_hash_map<VariableId, double>& ModelStorage::linear_objective()
1022 const {
1023 return linear_objective_;
1024}
1025
1026const absl::flat_hash_map<std::pair<VariableId, VariableId>, double>&
1028 return quadratic_objective_;
1029}
1030
1031} // namespace math_opt
1032} // namespace operations_research
1033
1034#endif // OR_TOOLS_MATH_OPT_CORE_MODEL_STORAGE_H_
LinearConstraintId next_linear_constraint_id() const
void set_quadratic_objective_coefficient(VariableId first_variable, VariableId second_variable, double value)
std::vector< LinearConstraintId > linear_constraints() const
ModelStorage & operator=(const ModelStorage &)=delete
std::vector< VariableId > SortedVariables() const
double linear_objective_coefficient(VariableId variable) const
static absl::StatusOr< std::unique_ptr< ModelStorage > > FromModelProto(const ModelProto &model_proto)
std::vector< VariableId > SortedLinearObjectiveNonzeroVariables() const
double linear_constraint_coefficient(LinearConstraintId constraint, VariableId variable) const
void DeleteLinearConstraint(LinearConstraintId id)
void set_linear_objective_coefficient(VariableId variable, double value)
const absl::flat_hash_map< std::pair< LinearConstraintId, VariableId >, double > & linear_constraint_matrix() const
void DeleteUpdateTracker(UpdateTrackerId update_tracker)
std::vector< VariableId > variables() const
const absl::flat_hash_map< std::pair< VariableId, VariableId >, double > & quadratic_objective() const
double linear_constraint_lower_bound(LinearConstraintId id) const
bool is_quadratic_objective_coefficient_nonzero(VariableId first_variable, VariableId second_variable) const
ModelStorage(absl::string_view name="")
absl::Status ApplyUpdateProto(const ModelUpdateProto &update_proto)
std::optional< ModelUpdateProto > ExportModelUpdate(UpdateTrackerId update_tracker)
void set_linear_constraint_coefficient(LinearConstraintId constraint, VariableId variable, double value)
VariableId AddVariable(absl::string_view name="")
void set_variable_upper_bound(VariableId id, double upper_bound)
void set_variable_is_integer(VariableId id, bool is_integer)
bool has_linear_constraint(LinearConstraintId id) const
const std::string & variable_name(VariableId id) const
const absl::flat_hash_set< VariableId > & variables_in_linear_constraint(LinearConstraintId constraint)
void set_linear_constraint_upper_bound(LinearConstraintId id, double upper_bound)
void Checkpoint(UpdateTrackerId update_tracker)
double quadratic_objective_coefficient(VariableId first_variable, VariableId second_variable) const
void set_linear_constraint_lower_bound(LinearConstraintId id, double lower_bound)
double linear_constraint_upper_bound(LinearConstraintId id) const
ModelStorage(const ModelStorage &)=delete
double variable_lower_bound(VariableId id) const
const absl::flat_hash_set< LinearConstraintId > & linear_constraints_with_variable(VariableId variable)
bool is_linear_constraint_coefficient_nonzero(LinearConstraintId constraint, VariableId variable) const
bool is_variable_integer(VariableId id) const
const std::string & linear_constraint_name(LinearConstraintId id) const
std::unique_ptr< ModelStorage > Clone() const
LinearConstraintId AddLinearConstraint(absl::string_view name="")
const absl::flat_hash_map< VariableId, double > & linear_objective() const
std::vector< LinearConstraintId > SortedLinearConstraints() const
bool is_linear_objective_coefficient_nonzero(VariableId variable) const
double variable_upper_bound(VariableId id) const
void set_variable_lower_bound(VariableId id, double lower_bound)
int64_t b
int64_t a
CpModelProto const * model_proto
const std::string name
int64_t value
double upper_bound
double lower_bound
absl::Span< const double > coefficients
Definition: cleanup.h:22
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:29
std::pair< VariableId, VariableId > MakeOrderedPair(const VariableId a, const VariableId b)
DEFINE_STRONG_INT_TYPE(VariableId, int64_t)
Collection of objects used to extend the Constraint Solver library.
STL namespace.