OR-Tools  9.2
indexed_model.cc
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
15
16#include <algorithm>
17#include <memory>
18#include <string>
19#include <utility>
20#include <vector>
21
23#include "absl/container/flat_hash_map.h"
24#include "absl/container/flat_hash_set.h"
25#include "absl/memory/memory.h"
26#include "absl/strings/string_view.h"
27#include "absl/synchronization/mutex.h"
28#include "absl/types/optional.h"
29#include "absl/types/span.h"
34#include "ortools/math_opt/model.pb.h"
35#include "ortools/math_opt/model_update.pb.h"
36#include "ortools/math_opt/result.pb.h"
37#include "ortools/math_opt/solution.pb.h"
38#include "ortools/math_opt/sparse_containers.pb.h"
39
40namespace operations_research {
41namespace math_opt {
42
43namespace {
44
45template <typename K, typename V>
46std::vector<K> MapKeys(const absl::flat_hash_map<K, V>& in_map) {
47 std::vector<K> keys;
48 keys.reserve(in_map.size());
49 for (const auto& key_pair : in_map) {
50 keys.push_back(key_pair.first);
51 }
52 return keys;
53}
54
55template <typename K, typename V>
56std::vector<K> SortedMapKeys(const absl::flat_hash_map<K, V>& in_map) {
57 std::vector<K> keys = MapKeys(in_map);
58 std::sort(keys.begin(), keys.end());
59 return keys;
60}
61
62template <typename T>
63std::vector<T> SortedSetKeys(const absl::flat_hash_set<T>& in_set) {
64 std::vector<T> keys;
65 keys.reserve(in_set.size());
66 for (const auto& key : in_set) {
67 keys.push_back(key);
68 }
69 std::sort(keys.begin(), keys.end());
70 return keys;
71}
72
73// ids should be sorted.
74template <typename IdType>
75void AppendFromMapOrDefault(const absl::Span<const IdType> ids,
76 const absl::flat_hash_map<IdType, double>& values,
77 SparseDoubleVectorProto& sparse_vector) {
78 for (const IdType id : ids) {
79 sparse_vector.add_ids(id.value());
80 sparse_vector.add_values(gtl::FindWithDefault(values, id));
81 }
82}
83
84// ids should be sorted.
85template <typename IdType, typename IdIterable>
86void AppendFromMapIfPresent(const IdIterable& ids,
87 const absl::flat_hash_map<IdType, double>& values,
88 SparseDoubleVectorProto& sparse_vector) {
89 for (const IdType id : ids) {
90 const double* const double_value = gtl::FindOrNull(values, id);
91 if (double_value != nullptr) {
92 sparse_vector.add_ids(id.value());
93 sparse_vector.add_values(*double_value);
94 }
95 }
96}
97
98template <typename IdType, typename DataType>
99void AppendFromMap(const absl::flat_hash_set<IdType>& dirty_keys,
100 const absl::flat_hash_map<IdType, DataType>& values,
101 double DataType::*field,
102 SparseDoubleVectorProto& sparse_vector) {
103 for (const IdType id : SortedSetKeys(dirty_keys)) {
104 sparse_vector.add_ids(id.value());
105 sparse_vector.add_values(values.at(id).*field);
106 }
107}
108
109template <typename T>
110absl::flat_hash_map<T, BasisStatus> SparseBasisVectorToMap(
111 const SparseBasisStatusVector& sparse_vector) {
112 absl::flat_hash_map<T, BasisStatus> result;
113 CHECK_EQ(sparse_vector.ids_size(), sparse_vector.values_size());
114 result.reserve(sparse_vector.ids_size());
115 for (const auto [id, value] : MakeView(sparse_vector)) {
116 gtl::InsertOrDie(&result, T(id), static_cast<BasisStatus>(value));
117 }
118 return result;
119}
120
121} // namespace
122
124 const double upper_bound,
125 const bool is_integer,
126 const absl::string_view name) {
127 const VariableId result = next_variable_id_++;
128 VariableData& var_data = variables_[result];
129 var_data.lower_bound = lower_bound;
130 var_data.upper_bound = upper_bound;
131 var_data.is_integer = is_integer;
132 var_data.name = name;
133 if (!lazy_matrix_columns_.empty()) {
134 gtl::InsertOrDie(&lazy_matrix_columns_, result, {});
135 }
136 return result;
137}
138
139void IndexedModel::DeleteVariable(const VariableId id) {
140 CHECK(variables_.contains(id));
141 EnsureLazyMatrixColumns();
142 EnsureLazyMatrixRows();
143 linear_objective_.erase(id);
144 variables_.erase(id);
145 if (id < variables_checkpoint_) {
146 dirty_variable_deletes_.insert(id);
147 dirty_variable_lower_bounds_.erase(id);
148 dirty_variable_upper_bounds_.erase(id);
149 dirty_variable_is_integer_.erase(id);
150 dirty_linear_objective_coefficients_.erase(id);
151 }
152 for (const LinearConstraintId related_constraint :
153 lazy_matrix_columns_.at(id)) {
154 CHECK_GT(lazy_matrix_rows_.at(related_constraint).erase(id), 0);
155 CHECK_GT(linear_constraint_matrix_.erase({related_constraint, id}), 0);
156 if (id < variables_checkpoint_ &&
157 related_constraint < linear_constraints_checkpoint_) {
158 dirty_linear_constraint_matrix_keys_.erase({related_constraint, id});
159 }
160 }
161 CHECK_GT(lazy_matrix_columns_.erase(id), 0);
162}
163
164std::vector<VariableId> IndexedModel::variables() const {
165 return MapKeys(variables_);
166}
167
168std::vector<VariableId> IndexedModel::SortedVariables() const {
169 return SortedMapKeys(variables_);
170}
171
173 const double lower_bound, const double upper_bound,
174 const absl::string_view name) {
175 const LinearConstraintId result = next_linear_constraint_id_++;
176 LinearConstraintData& lin_con_data = linear_constraints_[result];
177 lin_con_data.lower_bound = lower_bound;
178 lin_con_data.upper_bound = upper_bound;
179 lin_con_data.name = name;
180 if (!lazy_matrix_rows_.empty()) {
181 gtl::InsertOrDie(&lazy_matrix_rows_, result, {});
182 }
183 return result;
184}
185
186void IndexedModel::DeleteLinearConstraint(const LinearConstraintId id) {
187 CHECK(linear_constraints_.contains(id));
188 EnsureLazyMatrixColumns();
189 EnsureLazyMatrixRows();
190 linear_constraints_.erase(id);
191 if (id < linear_constraints_checkpoint_) {
192 dirty_linear_constraint_deletes_.insert(id);
193 dirty_linear_constraint_lower_bounds_.erase(id);
194 dirty_linear_constraint_upper_bounds_.erase(id);
195 }
196 for (const VariableId related_variable : lazy_matrix_rows_.at(id)) {
197 CHECK_GT(lazy_matrix_columns_.at(related_variable).erase(id), 0);
198 CHECK_GT(linear_constraint_matrix_.erase({id, related_variable}), 0);
199 if (id < linear_constraints_checkpoint_ &&
200 related_variable < variables_checkpoint_) {
201 dirty_linear_constraint_matrix_keys_.erase({id, related_variable});
202 }
203 }
204 CHECK_GT(lazy_matrix_rows_.erase(id), 0);
205}
206
207std::vector<LinearConstraintId> IndexedModel::linear_constraints() const {
208 return MapKeys(linear_constraints_);
209}
210
211std::vector<LinearConstraintId> IndexedModel::SortedLinearConstraints() const {
212 return SortedMapKeys(linear_constraints_);
213}
214
216 const {
217 return SortedMapKeys(linear_objective_);
218}
219
220void IndexedModel::AppendVariable(const VariableId id,
221 VariablesProto& variables_proto) const {
222 const VariableData& var_data = variables_.at(id);
223 variables_proto.add_ids(id.value());
224 variables_proto.add_lower_bounds(var_data.lower_bound);
225 variables_proto.add_upper_bounds(var_data.upper_bound);
226 variables_proto.add_integers(var_data.is_integer);
227 variables_proto.add_names(var_data.name);
228}
229
230void IndexedModel::AppendLinearConstraint(
231 const LinearConstraintId id,
232 LinearConstraintsProto& linear_constraints_proto) const {
233 const LinearConstraintData& con_impl = linear_constraints_.at(id);
234 linear_constraints_proto.add_ids(id.value());
235 linear_constraints_proto.add_lower_bounds(con_impl.lower_bound);
236 linear_constraints_proto.add_upper_bounds(con_impl.upper_bound);
237 linear_constraints_proto.add_names(con_impl.name);
238}
239
240void IndexedModel::ExportLinearConstraintMatrix(
241 const absl::Span<const std::pair<LinearConstraintId, VariableId>> entries,
242 SparseDoubleMatrixProto& matrix) const {
243 matrix.mutable_row_ids()->Reserve(entries.size());
244 matrix.mutable_column_ids()->Reserve(entries.size());
245 matrix.mutable_coefficients()->Reserve(entries.size());
246 for (const auto [constraint_id, variable_id] : entries) {
247 matrix.add_row_ids(constraint_id.value());
248 matrix.add_column_ids(variable_id.value());
249 matrix.add_coefficients(gtl::FindWithDefault(linear_constraint_matrix_,
250 {constraint_id, variable_id}));
251 }
252}
253
254ModelProto IndexedModel::ExportModel() const {
255 ModelProto result;
256 result.set_name(name_);
257 // Export the variables.
258 for (const VariableId variable : SortedMapKeys(variables_)) {
259 AppendVariable(variable, *result.mutable_variables());
260 }
261
262 // Pull out the objective.
263 result.mutable_objective()->set_maximize(is_maximize_);
264 result.mutable_objective()->set_offset(objective_offset_);
265 AppendFromMapOrDefault<VariableId>(
266 SortedMapKeys(linear_objective_), linear_objective_,
267 *result.mutable_objective()->mutable_linear_coefficients());
268
269 // Pull out the linear constraints.
270 for (const LinearConstraintId con : SortedMapKeys(linear_constraints_)) {
271 AppendLinearConstraint(con, *result.mutable_linear_constraints());
272 }
273
274 // Pull out the constraint matrix.
275 ExportLinearConstraintMatrix(SortedMapKeys(linear_constraint_matrix_),
276 *result.mutable_linear_constraint_matrix());
277 return result;
278}
279
280absl::optional<ModelUpdateProto> IndexedModel::ExportSharedModelUpdate() {
281 // We must detect the empty case to prevent unneeded copies and merging in
282 // UpdateTracker::ExportModelUpdate().
283 if (variables_checkpoint_ == next_variable_id_ &&
284 linear_constraints_checkpoint_ == next_linear_constraint_id_ &&
285 !dirty_objective_direction_ && !dirty_objective_offset_ &&
286 dirty_variable_deletes_.empty() && dirty_variable_lower_bounds_.empty() &&
287 dirty_variable_upper_bounds_.empty() &&
288 dirty_variable_is_integer_.empty() &&
289 dirty_linear_objective_coefficients_.empty() &&
290 dirty_linear_constraint_deletes_.empty() &&
291 dirty_linear_constraint_lower_bounds_.empty() &&
292 dirty_linear_constraint_upper_bounds_.empty() &&
293 dirty_linear_constraint_matrix_keys_.empty()) {
294 return absl::nullopt;
295 }
296
297 // TODO(user): these are used to efficiently extract the constraint matrix
298 // update, but it would be good to avoid calling these because they result in
299 // a large allocation.
300 EnsureLazyMatrixRows();
301 EnsureLazyMatrixColumns();
302
303 ModelUpdateProto result;
304
305 // Variable/constraint deletions.
306 for (const VariableId del_var : SortedSetKeys(dirty_variable_deletes_)) {
307 result.add_deleted_variable_ids(del_var.value());
308 }
309 for (const LinearConstraintId del_lin_con :
310 SortedSetKeys(dirty_linear_constraint_deletes_)) {
311 result.add_deleted_linear_constraint_ids(del_lin_con.value());
312 }
313
314 // Update the variables.
315 auto var_updates = result.mutable_variable_updates();
316 AppendFromMap(dirty_variable_lower_bounds_, variables_,
318 *var_updates->mutable_lower_bounds());
319 AppendFromMap(dirty_variable_upper_bounds_, variables_,
321 *var_updates->mutable_upper_bounds());
322
323 for (const VariableId integer_var :
324 SortedSetKeys(dirty_variable_is_integer_)) {
325 var_updates->mutable_integers()->add_ids(integer_var.value());
326 var_updates->mutable_integers()->add_values(
327 variables_.at(integer_var).is_integer);
328 }
329 for (VariableId new_id = variables_checkpoint_; new_id < next_variable_id_;
330 ++new_id) {
331 if (variables_.contains(new_id)) {
332 AppendVariable(new_id, *result.mutable_new_variables());
333 }
334 }
335
336 // Update the objective
337 auto obj_updates = result.mutable_objective_updates();
338 if (dirty_objective_direction_) {
339 obj_updates->set_direction_update(is_maximize_);
340 }
341 if (dirty_objective_offset_) {
342 obj_updates->set_offset_update(objective_offset_);
343 }
344 AppendFromMapOrDefault<VariableId>(
345 SortedSetKeys(dirty_linear_objective_coefficients_), linear_objective_,
346 *obj_updates->mutable_linear_coefficients());
347 // TODO(b/182567749): Once StrongInt is in absl, use
348 // AppendFromMapIfPresent<VariableId>(
349 // MakeStrongIntRange(variables_checkpoint_, next_variable_id_),
350 // linear_objective_, *obj_updates->mutable_linear_coefficients());
351 for (VariableId var_id = variables_checkpoint_; var_id < next_variable_id_;
352 ++var_id) {
353 const double* const double_value =
354 gtl::FindOrNull(linear_objective_, var_id);
355 if (double_value != nullptr) {
356 obj_updates->mutable_linear_coefficients()->add_ids(var_id.value());
357 obj_updates->mutable_linear_coefficients()->add_values(*double_value);
358 }
359 }
360
361 // Update the linear constraints
362 auto lin_con_updates = result.mutable_linear_constraint_updates();
363 AppendFromMap(dirty_linear_constraint_lower_bounds_, linear_constraints_,
365 *lin_con_updates->mutable_lower_bounds());
366 AppendFromMap(dirty_linear_constraint_upper_bounds_, linear_constraints_,
368 *lin_con_updates->mutable_upper_bounds());
369
370 for (LinearConstraintId new_id = linear_constraints_checkpoint_;
371 new_id < next_linear_constraint_id_; ++new_id) {
372 if (linear_constraints_.contains(new_id)) {
373 AppendLinearConstraint(new_id, *result.mutable_new_linear_constraints());
374 }
375 }
376
377 // Extract changes to the matrix of linear constraint coefficients
378 std::vector<std::pair<LinearConstraintId, VariableId>>
379 constraint_matrix_updates(dirty_linear_constraint_matrix_keys_.begin(),
380 dirty_linear_constraint_matrix_keys_.end());
381 for (VariableId new_var = variables_checkpoint_; new_var < next_variable_id_;
382 ++new_var) {
383 if (variables_.contains(new_var)) {
384 for (const LinearConstraintId lin_con :
385 lazy_matrix_columns_.at(new_var)) {
386 constraint_matrix_updates.emplace_back(lin_con, new_var);
387 }
388 }
389 }
390 for (LinearConstraintId new_lin_con = linear_constraints_checkpoint_;
391 new_lin_con < next_linear_constraint_id_; ++new_lin_con) {
392 if (linear_constraints_.contains(new_lin_con)) {
393 for (const VariableId var : lazy_matrix_rows_.at(new_lin_con)) {
394 // NOTE(user): we will do at most twice as much as needed here.
395 if (var < variables_checkpoint_) {
396 constraint_matrix_updates.emplace_back(new_lin_con, var);
397 }
398 }
399 }
400 }
401 std::sort(constraint_matrix_updates.begin(), constraint_matrix_updates.end());
402 ExportLinearConstraintMatrix(
403 constraint_matrix_updates,
404 *result.mutable_linear_constraint_matrix_updates());
405
406 // Named returned value optimization (NRVO) does not apply here since the
407 // return type if not the same type as `result`. To make things clear, we
408 // explicitly call the constructor here.
409 return {std::move(result)};
410}
411
412void IndexedModel::EnsureLazyMatrixColumns() {
413 if (lazy_matrix_columns_.empty()) {
414 for (const auto& var_pair : variables_) {
415 lazy_matrix_columns_.insert({var_pair.first, {}});
416 }
417 for (const auto& mat_entry : linear_constraint_matrix_) {
418 lazy_matrix_columns_.at(mat_entry.first.second)
419 .insert(mat_entry.first.first);
420 }
421 }
422}
423
424void IndexedModel::EnsureLazyMatrixRows() {
425 if (lazy_matrix_rows_.empty()) {
426 for (const auto& lin_con_pair : linear_constraints_) {
427 lazy_matrix_rows_.insert({lin_con_pair.first, {}});
428 }
429 for (const auto& mat_entry : linear_constraint_matrix_) {
430 lazy_matrix_rows_.at(mat_entry.first.first)
431 .insert(mat_entry.first.second);
432 }
433 }
434}
435
436void IndexedModel::SharedCheckpoint() {
437 variables_checkpoint_ = next_variable_id_;
438 linear_constraints_checkpoint_ = next_linear_constraint_id_;
439 dirty_objective_direction_ = false;
440 dirty_objective_offset_ = false;
441
442 dirty_variable_deletes_.clear();
443 dirty_variable_lower_bounds_.clear();
444 dirty_variable_upper_bounds_.clear();
445 dirty_variable_is_integer_.clear();
446
447 dirty_linear_objective_coefficients_.clear();
448
449 dirty_linear_constraint_deletes_.clear();
450 dirty_linear_constraint_lower_bounds_.clear();
451 dirty_linear_constraint_upper_bounds_.clear();
452 dirty_linear_constraint_matrix_keys_.clear();
453}
454
456 const SolveResultProto& solve_result) {
457 IndexedSolutions solutions;
458 for (const PrimalSolutionProto& primal_solution :
459 solve_result.primal_solutions()) {
462 MakeView(primal_solution.variable_values()).as_map<VariableId>();
463 p.objective_value = primal_solution.objective_value();
464 solutions.primal_solutions.push_back(std::move(p));
465 }
466 for (const PrimalRayProto& primal_ray : solve_result.primal_rays()) {
468 pr.variable_values =
469 MakeView(primal_ray.variable_values()).as_map<VariableId>();
470 solutions.primal_rays.push_back(std::move(pr));
471 }
472 for (const DualSolutionProto& dual_solution : solve_result.dual_solutions()) {
474 d.reduced_costs =
475 MakeView(dual_solution.reduced_costs()).as_map<VariableId>();
476 d.dual_values =
477 MakeView(dual_solution.dual_values()).as_map<LinearConstraintId>();
478 d.objective_value = dual_solution.objective_value();
479 solutions.dual_solutions.push_back(std::move(d));
480 }
481 for (const DualRayProto& dual_ray : solve_result.dual_rays()) {
483 dr.reduced_costs = MakeView(dual_ray.reduced_costs()).as_map<VariableId>();
484 dr.dual_values =
485 MakeView(dual_ray.dual_values()).as_map<LinearConstraintId>();
486 solutions.dual_rays.push_back(std::move(dr));
487 }
488 for (const BasisProto& basis : solve_result.basis()) {
489 IndexedBasis indexed_basis;
490 indexed_basis.constraint_status =
491 SparseBasisVectorToMap<LinearConstraintId>(basis.constraint_status());
492 indexed_basis.variable_status =
493 SparseBasisVectorToMap<VariableId>(basis.variable_status());
494 solutions.basis.push_back(std::move(indexed_basis));
495 }
496 return solutions;
497}
498
499std::unique_ptr<IndexedModel::UpdateTracker> IndexedModel::NewUpdateTracker() {
500 // UpdateTracker constructor will call UpdateTracker::Checkpoint() that
501 // flushes the current update to all other trackers and updates the checkpoint
502 // of this model to the current state of the model as returned by
503 // ExportModel().
504 return absl::WrapUnique(new UpdateTracker(*this));
505}
506
507IndexedModel::UpdateTracker::UpdateTracker(IndexedModel& indexed_model)
508 : indexed_model_(indexed_model) {
509 absl::MutexLock lock(&indexed_model_.update_trackers_lock_);
510 CHECK(indexed_model_.update_trackers_.insert(this).second);
511 CheckpointLocked();
512}
513
515 absl::MutexLock lock(&indexed_model_.update_trackers_lock_);
516 CHECK(indexed_model_.update_trackers_.erase(this));
517}
518
519absl::optional<ModelUpdateProto>
521 absl::MutexLock lock(&indexed_model_.update_trackers_lock_);
522
523 // No updates have been pushed, the checkpoint of this tracker is in sync with
524 // the shared checkpoint of IndexedModel. We can return the IndexedModel
525 // shared update without merging.
526 if (updates_.empty()) {
527 return indexed_model_.ExportSharedModelUpdate();
528 }
529
530 // Find all trackers with the same checkpoint. By construction, all trackers
531 // that have the same first update also share all next updates.
532 std::vector<UpdateTracker*> all_trackers_at_checkpoint;
533 bool found_this = false;
534 for (UpdateTracker* const tracker : indexed_model_.update_trackers_) {
535 if (!tracker->updates_.empty() &&
536 tracker->updates_.front() == updates_.front()) {
537 // Note that we set `found_this` inside the if branch to make sure we also
538 // detect a bug in this code that would not include `this` in the list of
539 // trackers.
540 if (tracker == this) {
541 found_this = true;
542 }
543 all_trackers_at_checkpoint.push_back(tracker);
544
545 // Validate that we have the same updates in debug mode only. In optimized
546 // mode, only test the size of the updates_ vectors.
547 CHECK_EQ(updates_.size(), tracker->updates_.size());
548 if (DEBUG_MODE) {
549 for (int i = 0; i < updates_.size(); ++i) {
550 CHECK_EQ(updates_[i], tracker->updates_[i])
551 << "Two trackers have the same checkpoint but different updates.";
552 }
553 }
554 }
555 }
556 CHECK(found_this);
557
558 // Possible optimizations here:
559 //
560 // * Maybe optimize the case where the first update is singly used by `this`
561 // and use it as starting point instead of making a copy. This may be more
562 // complicated if it is shared with multiple trackers since in that case we
563 // must make sure to only update the shared instance if and only if only
564 // trackers have a pointer to it, not external code (i.e. its use count is
565 // the same as the number of trackers).
566 //
567 // * Use n-way merge here if the performances justify it.
568 const auto merge = std::make_shared<ModelUpdateProto>();
569 for (const auto& update : updates_) {
570 MergeIntoUpdate(/*from=*/*update, /*into=*/*merge);
571 }
572
573 // Push the merge to all trackers that have the same checkpoint (including
574 // this tracker).
575 for (UpdateTracker* const tracker : all_trackers_at_checkpoint) {
576 tracker->updates_.clear();
577 tracker->updates_.push_back(merge);
578 }
579
580 ModelUpdateProto update = *merge;
581 const absl::optional<ModelUpdateProto> pending_update =
582 indexed_model_.ExportSharedModelUpdate();
583 if (pending_update) {
584 MergeIntoUpdate(/*from=*/*pending_update, /*into=*/update);
585 }
586
587 // Named returned value optimization (NRVO) does not apply here since the
588 // return type if not the same type as `result`. To make things clear, we
589 // explicitly call the constructor here.
590 return {std::move(update)};
591}
592
594 absl::MutexLock lock(&indexed_model_.update_trackers_lock_);
595
596 CheckpointLocked();
597}
598
599void IndexedModel::UpdateTracker::CheckpointLocked() {
600 // Optimize the case where we have a single tracker and we don't want to
601 // update it. In that case we don't need to update trackers since we would
602 // only update this one and clear it immediately.
603 if (indexed_model_.update_trackers_.size() == 1) {
604 CHECK(*indexed_model_.update_trackers_.begin() == this);
605 } else {
606 absl::optional<ModelUpdateProto> update =
607 indexed_model_.ExportSharedModelUpdate();
608 if (update) {
609 const auto shared_update =
610 std::make_shared<ModelUpdateProto>(*std::move(update));
611
612 bool found_this = false;
613 for (UpdateTracker* const tracker : indexed_model_.update_trackers_) {
614 if (tracker == this) {
615 found_this = true;
616 }
617 tracker->updates_.push_back(shared_update);
618 }
619 CHECK(found_this);
620 }
621 }
622 indexed_model_.SharedCheckpoint();
623 updates_.clear();
624}
625
626} // namespace math_opt
627} // namespace operations_research
#define CHECK(condition)
Definition: base/logging.h:492
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:699
#define CHECK_GT(val1, val2)
Definition: base/logging.h:704
~UpdateTracker() ABSL_LOCKS_EXCLUDED(indexed_model_.update_trackers_lock_)
void Checkpoint() ABSL_LOCKS_EXCLUDED(indexed_model_.update_trackers_lock_)
absl::optional< ModelUpdateProto > ExportModelUpdate() ABSL_LOCKS_EXCLUDED(indexed_model_.update_trackers_lock_)
std::vector< LinearConstraintId > linear_constraints() const
std::vector< VariableId > SortedVariables() const
std::vector< VariableId > SortedLinearObjectiveNonzeroVariables() const
void DeleteLinearConstraint(LinearConstraintId id)
std::vector< VariableId > variables() const
VariableId AddVariable(absl::string_view name="")
LinearConstraintId AddLinearConstraint(absl::string_view name="")
std::vector< LinearConstraintId > SortedLinearConstraints() const
std::unique_ptr< UpdateTracker > NewUpdateTracker()
const std::string name
int64_t value
IntVar * var
Definition: expr_array.cc:1874
double upper_bound
double lower_bound
const bool DEBUG_MODE
Definition: macros.h:24
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:154
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
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:60
void MergeIntoUpdate(const ModelUpdateProto &from, ModelUpdateProto &into)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
IndexedSolutions IndexedSolutionsFromProto(const SolveResultProto &solve_result)
Collection of objects used to extend the Constraint Solver library.
absl::flat_hash_map< VariableId, BasisStatus > variable_status
absl::flat_hash_map< LinearConstraintId, BasisStatus > constraint_status
absl::flat_hash_map< VariableId, double > reduced_costs
absl::flat_hash_map< LinearConstraintId, double > dual_values
absl::flat_hash_map< VariableId, double > reduced_costs
absl::flat_hash_map< LinearConstraintId, double > dual_values
absl::flat_hash_map< VariableId, double > variable_values
absl::flat_hash_map< VariableId, double > variable_values
std::vector< IndexedDualSolution > dual_solutions
std::vector< IndexedPrimalSolution > primal_solutions
std::vector< IndexedPrimalRay > primal_rays