OR-Tools  9.1
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 
22 #include "ortools/base/logging.h"
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"
30 #include "ortools/base/map_util.h"
31 #include "ortools/base/int_type.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 
40 namespace operations_research {
41 namespace math_opt {
42 
43 namespace {
44 
45 template <typename K, typename V>
46 std::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 
55 template <typename K, typename V>
56 std::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 
62 template <typename T>
63 std::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.
74 template <typename IdType>
75 void 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.
85 template <typename IdType, typename IdIterable>
86 void 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 
98 template <typename IdType, typename DataType>
99 void 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 
109 template <typename T>
110 absl::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 
123 VariableId IndexedModel::AddVariable(const double lower_bound,
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 
139 void 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 
164 std::vector<VariableId> IndexedModel::variables() const {
165  return MapKeys(variables_);
166 }
167 
168 std::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 
186 void 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 
207 std::vector<LinearConstraintId> IndexedModel::linear_constraints() const {
208  return MapKeys(linear_constraints_);
209 }
210 
211 std::vector<LinearConstraintId> IndexedModel::SortedLinearConstraints() const {
212  return SortedMapKeys(linear_constraints_);
213 }
214 
216  const {
217  return SortedMapKeys(linear_objective_);
218 }
219 
220 void 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 
230 void 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 
240 void 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 
254 ModelProto 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 
280 absl::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 
412 void 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 
424 void 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 
436 void 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()) {
461  p.variable_values =
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()) {
467  IndexedPrimalRay pr;
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()) {
482  IndexedDualRay dr;
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 
499 std::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 
507 IndexedModel::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 
519 absl::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 
599 void 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
std::vector< IndexedPrimalSolution > primal_solutions
#define CHECK(condition)
Definition: base/logging.h:491
std::vector< VariableId > variables() const
LinearConstraintId AddLinearConstraint(absl::string_view name="")
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
const bool DEBUG_MODE
Definition: macros.h:24
~UpdateTracker() ABSL_LOCKS_EXCLUDED(indexed_model_.update_trackers_lock_)
absl::flat_hash_map< VariableId, BasisStatus > variable_status
absl::flat_hash_map< VariableId, double > reduced_costs
#define CHECK_GT(val1, val2)
Definition: base/logging.h:703
VariableId AddVariable(absl::string_view name="")
const std::string name
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:154
absl::flat_hash_map< VariableId, double > reduced_costs
absl::flat_hash_map< VariableId, double > variable_values
std::vector< IndexedDualSolution > dual_solutions
std::vector< LinearConstraintId > SortedLinearConstraints() const
void MergeIntoUpdate(const ModelUpdateProto &from, ModelUpdateProto &into)
std::unique_ptr< UpdateTracker > NewUpdateTracker()
double upper_bound
absl::optional< ModelUpdateProto > ExportModelUpdate() ABSL_LOCKS_EXCLUDED(indexed_model_.update_trackers_lock_)
absl::flat_hash_map< LinearConstraintId, double > dual_values
std::vector< VariableId > SortedVariables() const
double lower_bound
absl::flat_hash_map< LinearConstraintId, BasisStatus > constraint_status
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
void Checkpoint() ABSL_LOCKS_EXCLUDED(indexed_model_.update_trackers_lock_)
void DeleteLinearConstraint(LinearConstraintId id)
IndexedSolutions IndexedSolutionsFromProto(const SolveResultProto &solve_result)
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
absl::flat_hash_map< LinearConstraintId, double > dual_values
std::vector< LinearConstraintId > linear_constraints() const
absl::flat_hash_map< VariableId, double > variable_values
Collection of objects used to extend the Constraint Solver library.
std::vector< VariableId > SortedLinearObjectiveNonzeroVariables() const
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:60
IntVar * var
Definition: expr_array.cc:1874
std::vector< IndexedPrimalRay > primal_rays
int64_t value