OR-Tools  9.6
model_storage.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 <cstdint>
17 #include <memory>
18 #include <optional>
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/container/flat_hash_map.h"
24 #include "absl/container/flat_hash_set.h"
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
27 #include "absl/strings/string_view.h"
28 #include "absl/types/span.h"
29 #include "absl/log/check.h"
30 #include "ortools/base/map_util.h"
35 #include "ortools/math_opt/model.pb.h"
36 #include "ortools/math_opt/model_update.pb.h"
37 #include "ortools/math_opt/sparse_containers.pb.h"
45 
46 namespace operations_research {
47 namespace math_opt {
48 
49 absl::StatusOr<std::unique_ptr<ModelStorage>> ModelStorage::FromModelProto(
50  const ModelProto& model_proto) {
51  // We don't check names since ModelStorage does not do so before exporting
52  // models. Thus a model built by ModelStorage can contain duplicated
53  // names. And since we use FromModelProto() to implement Clone(), we must make
54  // sure duplicated names don't fail.
55  RETURN_IF_ERROR(ValidateModel(model_proto, /*check_names=*/false).status());
56 
57  auto storage = std::make_unique<ModelStorage>(model_proto.name());
58 
59  // Add variables.
60  storage->AddVariables(model_proto.variables());
61 
62  // Set the objective.
63  storage->set_is_maximize(model_proto.objective().maximize());
64  storage->set_objective_offset(model_proto.objective().offset());
65  storage->UpdateLinearObjectiveCoefficients(
66  model_proto.objective().linear_coefficients());
67  storage->UpdateQuadraticObjectiveCoefficients(
68  model_proto.objective().quadratic_coefficients());
69 
70  // Add linear constraints.
71  storage->AddLinearConstraints(model_proto.linear_constraints());
72 
73  // Set the linear constraints coefficients.
74  storage->UpdateLinearConstraintCoefficients(
75  model_proto.linear_constraint_matrix());
76 
77  // Add quadratic constraints.
78  storage->quadratic_constraints_.AddConstraints(
79  model_proto.quadratic_constraints());
80 
81  // Add SOS constraints.
82  storage->sos1_constraints_.AddConstraints(model_proto.sos1_constraints());
83  storage->sos2_constraints_.AddConstraints(model_proto.sos2_constraints());
84 
85  // Add indicator constraints.
86  storage->indicator_constraints_.AddConstraints(
87  model_proto.indicator_constraints());
88 
89  return storage;
90 }
91 
92 void ModelStorage::UpdateLinearObjectiveCoefficients(
93  const SparseDoubleVectorProto& coefficients) {
94  for (const auto [var_id, value] : MakeView(coefficients)) {
95  set_linear_objective_coefficient(VariableId(var_id), value);
96  }
97 }
98 
99 void ModelStorage::UpdateQuadraticObjectiveCoefficients(
100  const SparseDoubleMatrixProto& coefficients) {
101  for (int i = 0; i < coefficients.row_ids_size(); ++i) {
102  // This call is valid since this is an upper triangular matrix; there is no
103  // duplicated terms.
104  set_quadratic_objective_coefficient(VariableId(coefficients.row_ids(i)),
105  VariableId(coefficients.column_ids(i)),
106  coefficients.coefficients(i));
107  }
108 }
109 
110 void ModelStorage::UpdateLinearConstraintCoefficients(
111  const SparseDoubleMatrixProto& coefficients) {
112  for (int i = 0; i < coefficients.row_ids_size(); ++i) {
113  // This call is valid since there are no duplicated pairs.
115  LinearConstraintId(coefficients.row_ids(i)),
116  VariableId(coefficients.column_ids(i)), coefficients.coefficients(i));
117  }
118 }
119 
120 std::unique_ptr<ModelStorage> ModelStorage::Clone(
121  const std::optional<absl::string_view> new_name) const {
122  ModelProto model_proto = ExportModel();
123  if (new_name.has_value()) {
124  model_proto.set_name(std::string(*new_name));
125  }
126  absl::StatusOr<std::unique_ptr<ModelStorage>> clone =
128  // Unless there is a very serious bug, a model exported by ExportModel()
129  // should always be valid.
130  CHECK_OK(clone.status());
131 
132  // Update the next ids so that the clone does not reused any deleted id from
133  // the original.
134  clone.value()->ensure_next_variable_id_at_least(next_variable_id());
135  clone.value()->ensure_next_linear_constraint_id_at_least(
137  clone.value()->ensure_next_constraint_id_at_least(
138  next_constraint_id<QuadraticConstraintId>());
139  clone.value()->ensure_next_constraint_id_at_least(
140  next_constraint_id<Sos1ConstraintId>());
141  clone.value()->ensure_next_constraint_id_at_least(
142  next_constraint_id<Sos2ConstraintId>());
143  clone.value()->ensure_next_constraint_id_at_least(
144  next_constraint_id<IndicatorConstraintId>());
145 
146  return std::move(clone).value();
147 }
148 
149 VariableId ModelStorage::AddVariable(const double lower_bound,
150  const double upper_bound,
151  const bool is_integer,
152  const absl::string_view name) {
153  return variables_.Add(lower_bound, upper_bound, is_integer, name);
154 }
155 
156 void ModelStorage::AddVariables(const VariablesProto& variables) {
157  const bool has_names = !variables.names().empty();
158  for (int v = 0; v < variables.ids_size(); ++v) {
159  // Make sure the ids of the new Variables in the model match the proto,
160  // which are potentially non-consecutive (note that variables has been
161  // validated).
162  ensure_next_variable_id_at_least(VariableId(variables.ids(v)));
163  AddVariable(variables.lower_bounds(v),
164  /*upper_bound=*/variables.upper_bounds(v),
165  /*is_integer=*/variables.integers(v),
166  has_names ? variables.names(v) : absl::string_view());
167  }
168 }
169 
170 void ModelStorage::DeleteVariable(const VariableId id) {
171  CHECK(variables_.contains(id));
172  const auto& trackers = update_trackers_.GetUpdatedTrackers();
173  // Reuse output of GetUpdatedTrackers() only once to ensure a consistent view,
174  // do not call UpdateAndGetLinearConstraintDiffs() etc.
175  objective_.DeleteVariable(
176  id,
177  MakeUpdateDataFieldRange<&UpdateTrackerData::dirty_objective>(trackers));
178  linear_constraints_.DeleteVariable(
179  id,
180  MakeUpdateDataFieldRange<&UpdateTrackerData::dirty_linear_constraints>(
181  trackers));
182  quadratic_constraints_.DeleteVariable(id);
183  sos1_constraints_.DeleteVariable(id);
184  sos2_constraints_.DeleteVariable(id);
185  indicator_constraints_.DeleteVariable(id);
186  variables_.Delete(
187  id,
188  MakeUpdateDataFieldRange<&UpdateTrackerData::dirty_variables>(trackers));
189 }
190 
191 std::vector<VariableId> ModelStorage::variables() const {
192  return variables_.Variables();
193 }
194 
195 std::vector<VariableId> ModelStorage::SortedVariables() const {
196  return variables_.SortedVariables();
197 }
198 
200  const double lower_bound, const double upper_bound,
201  const absl::string_view name) {
202  return linear_constraints_.Add(lower_bound, upper_bound, name);
203 }
204 
205 void ModelStorage::AddLinearConstraints(
206  const LinearConstraintsProto& linear_constraints) {
207  const bool has_names = !linear_constraints.names().empty();
208  for (int c = 0; c < linear_constraints.ids_size(); ++c) {
209  // Make sure the ids of the new linear constraints in the model match the
210  // proto, which are potentially non-consecutive (note that
211  // linear_constraints has been validated).
213  LinearConstraintId(linear_constraints.ids(c)));
214  // This call is valid since ids are unique and increasing.
216  /*lower_bound=*/linear_constraints.lower_bounds(c),
217  /*upper_bound=*/linear_constraints.upper_bounds(c),
218  has_names ? linear_constraints.names(c) : absl::string_view());
219  }
220 }
221 
222 void ModelStorage::DeleteLinearConstraint(const LinearConstraintId id) {
223  CHECK(linear_constraints_.contains(id));
224  linear_constraints_.Delete(id, UpdateAndGetLinearConstraintDiffs());
225 }
226 
227 std::vector<LinearConstraintId> ModelStorage::LinearConstraints() const {
228  return linear_constraints_.LinearConstraints();
229 }
230 
231 std::vector<LinearConstraintId> ModelStorage::SortedLinearConstraints() const {
232  return linear_constraints_.SortedLinearConstraints();
233 }
234 
235 ModelProto ModelStorage::ExportModel() const {
236  ModelProto result;
237  result.set_name(name_);
238  *result.mutable_variables() = variables_.Proto();
239  *result.mutable_objective() = objective_.Proto();
240  {
241  auto [constraints, matrix] = linear_constraints_.Proto();
242  *result.mutable_linear_constraints() = std::move(constraints);
243  *result.mutable_linear_constraint_matrix() = std::move(matrix);
244  }
245  *result.mutable_quadratic_constraints() = quadratic_constraints_.Proto();
246  *result.mutable_sos1_constraints() = sos1_constraints_.Proto();
247  *result.mutable_sos2_constraints() = sos2_constraints_.Proto();
248  *result.mutable_indicator_constraints() = indicator_constraints_.Proto();
249  return result;
250 }
251 
252 std::optional<ModelUpdateProto>
253 ModelStorage::UpdateTrackerData::ExportModelUpdate(
254  const ModelStorage& storage) const {
255  // We must detect the empty case to prevent unneeded copies and merging in
256  // ExportModelUpdate().
257 
258  if (storage.variables_.diff_is_empty(dirty_variables) &&
259  storage.objective_.diff_is_empty(dirty_objective) &&
260  storage.linear_constraints_.diff_is_empty(dirty_linear_constraints) &&
261  storage.quadratic_constraints_.diff_is_empty(
262  dirty_quadratic_constraints) &&
263  storage.sos1_constraints_.diff_is_empty(dirty_sos1_constraints) &&
264  storage.sos2_constraints_.diff_is_empty(dirty_sos2_constraints) &&
265  storage.indicator_constraints_.diff_is_empty(
266  dirty_indicator_constraints)) {
267  return std::nullopt;
268  }
269 
270  ModelUpdateProto result;
271 
272  // Variable/constraint deletions.
273  {
274  VariableStorage::UpdateResult variable_update =
275  storage.variables_.Update(dirty_variables);
276  *result.mutable_deleted_variable_ids() = std::move(variable_update.deleted);
277  *result.mutable_variable_updates() = std::move(variable_update.updates);
278  *result.mutable_new_variables() = std::move(variable_update.creates);
279  }
280  const std::vector<VariableId> new_variables =
281  storage.variables_.VariablesFrom(dirty_variables.checkpoint);
282 
283  // Linear constraint updates
284  {
285  LinearConstraintStorage::UpdateResult lin_con_update =
286  storage.linear_constraints_.Update(
287  dirty_linear_constraints, dirty_variables.deleted, new_variables);
288  *result.mutable_deleted_linear_constraint_ids() =
289  std::move(lin_con_update.deleted);
290  *result.mutable_linear_constraint_updates() =
291  std::move(lin_con_update.updates);
292  *result.mutable_new_linear_constraints() =
293  std::move(lin_con_update.creates);
294  *result.mutable_linear_constraint_matrix_updates() =
295  std::move(lin_con_update.matrix_updates);
296  }
297 
298  // Quadratic constraint updates
299  *result.mutable_quadratic_constraint_updates() =
300  storage.quadratic_constraints_.Update(dirty_quadratic_constraints);
301 
302  // SOS constraint updates
303  *result.mutable_sos1_constraint_updates() =
304  storage.sos1_constraints_.Update(dirty_sos1_constraints);
305  *result.mutable_sos2_constraint_updates() =
306  storage.sos2_constraints_.Update(dirty_sos2_constraints);
307 
308  // Indicator constraint updates
309  *result.mutable_indicator_constraint_updates() =
310  storage.indicator_constraints_.Update(dirty_indicator_constraints);
311 
312  // Update the objective
313  *result.mutable_objective_updates() = storage.objective_.Update(
314  dirty_objective, dirty_variables.deleted, new_variables);
315  // Note: Named returned value optimization (NRVO) does not apply here.
316  return {std::move(result)};
317 }
318 
319 void ModelStorage::UpdateTrackerData::AdvanceCheckpoint(
320  const ModelStorage& storage) {
321  storage.variables_.AdvanceCheckpointInDiff(dirty_variables);
322  storage.objective_.AdvanceCheckpointInDiff(dirty_variables.checkpoint,
323  dirty_objective);
324  storage.linear_constraints_.AdvanceCheckpointInDiff(
325  dirty_variables.checkpoint, dirty_linear_constraints);
326  storage.quadratic_constraints_.AdvanceCheckpointInDiff(
327  dirty_quadratic_constraints);
328  storage.sos1_constraints_.AdvanceCheckpointInDiff(dirty_sos1_constraints);
329  storage.sos2_constraints_.AdvanceCheckpointInDiff(dirty_sos2_constraints);
330  storage.indicator_constraints_.AdvanceCheckpointInDiff(
331  dirty_indicator_constraints);
332 }
333 
335  return update_trackers_.NewUpdateTracker(
336  variables_, linear_constraints_, quadratic_constraints_,
337  sos1_constraints_, sos2_constraints_, indicator_constraints_);
338 }
339 
340 void ModelStorage::DeleteUpdateTracker(const UpdateTrackerId update_tracker) {
341  update_trackers_.DeleteUpdateTracker(update_tracker);
342 }
343 
344 std::optional<ModelUpdateProto> ModelStorage::ExportModelUpdate(
345  const UpdateTrackerId update_tracker) const {
346  return update_trackers_.GetData(update_tracker).ExportModelUpdate(*this);
347 }
348 
349 void ModelStorage::AdvanceCheckpoint(UpdateTrackerId update_tracker) {
350  update_trackers_.GetData(update_tracker).AdvanceCheckpoint(*this);
351 }
352 
354  const ModelUpdateProto& update_proto) {
355  // Check the update first.
356  {
357  // Do not check for duplicate names, as with FromModelProto();
358  ModelSummary summary(/*check_names=*/false);
359  // IdNameBiMap requires Insert() calls to be in sorted id order.
360  for (const VariableId id : SortedVariables()) {
361  RETURN_IF_ERROR(summary.variables.Insert(id.value(), variable_name(id)))
362  << "invalid variable id in model";
363  }
365  summary.variables.SetNextFreeId(variables_.next_id().value()));
366  for (const LinearConstraintId id : SortedLinearConstraints()) {
368  id.value(), linear_constraint_name(id)))
369  << "invalid linear constraint id in model";
370  }
372  linear_constraints_.next_id().value()));
373  for (const auto id : SortedConstraints<QuadraticConstraintId>()) {
375  id.value(), quadratic_constraints_.data(id).name))
376  << "invalid quadratic constraint id in model";
377  }
379  quadratic_constraints_.next_id().value()));
380  for (const Sos1ConstraintId id : SortedConstraints<Sos1ConstraintId>()) {
382  id.value(), constraint_data(id).name()))
383  << "invalid SOS1 constraint id in model";
384  }
386  sos1_constraints_.next_id().value()));
387  for (const Sos2ConstraintId id : SortedConstraints<Sos2ConstraintId>()) {
389  id.value(), constraint_data(id).name()))
390  << "invalid SOS2 constraint id in model";
391  }
393  sos2_constraints_.next_id().value()));
394 
395  for (const IndicatorConstraintId id :
396  SortedConstraints<IndicatorConstraintId>()) {
398  id.value(), constraint_data(id).name));
399  }
401  indicator_constraints_.next_id().value()));
402 
403  RETURN_IF_ERROR(ValidateModelUpdate(update_proto, summary))
404  << "update not valid";
405  }
406 
407  // Remove deleted variables and constraints.
408  for (const int64_t v_id : update_proto.deleted_variable_ids()) {
409  DeleteVariable(VariableId(v_id));
410  }
411  for (const int64_t c_id : update_proto.deleted_linear_constraint_ids()) {
412  DeleteLinearConstraint(LinearConstraintId(c_id));
413  }
414  for (const int64_t c_id :
415  update_proto.quadratic_constraint_updates().deleted_constraint_ids()) {
416  DeleteAtomicConstraint(QuadraticConstraintId(c_id));
417  }
418  for (const int64_t c_id :
419  update_proto.sos1_constraint_updates().deleted_constraint_ids()) {
420  DeleteAtomicConstraint(Sos1ConstraintId(c_id));
421  }
422  for (const int64_t c_id :
423  update_proto.sos2_constraint_updates().deleted_constraint_ids()) {
424  DeleteAtomicConstraint(Sos2ConstraintId(c_id));
425  }
426  for (const int64_t c_id :
427  update_proto.indicator_constraint_updates().deleted_constraint_ids()) {
428  DeleteAtomicConstraint(IndicatorConstraintId(c_id));
429  }
430 
431  // Update existing variables' properties.
432  for (const auto [v_id, lb] :
433  MakeView(update_proto.variable_updates().lower_bounds())) {
434  set_variable_lower_bound(VariableId(v_id), lb);
435  }
436  for (const auto [v_id, ub] :
437  MakeView(update_proto.variable_updates().upper_bounds())) {
438  set_variable_upper_bound(VariableId(v_id), ub);
439  }
440  for (const auto [v_id, is_integer] :
441  MakeView(update_proto.variable_updates().integers())) {
442  set_variable_is_integer(VariableId(v_id), is_integer);
443  }
444 
445  // Update existing constraints' properties.
446  for (const auto [c_id, lb] :
447  MakeView(update_proto.linear_constraint_updates().lower_bounds())) {
448  set_linear_constraint_lower_bound(LinearConstraintId(c_id), lb);
449  }
450  for (const auto [c_id, ub] :
451  MakeView(update_proto.linear_constraint_updates().upper_bounds())) {
452  set_linear_constraint_upper_bound(LinearConstraintId(c_id), ub);
453  }
454 
455  // Add the new variables and constraints.
456  AddVariables(update_proto.new_variables());
457  AddLinearConstraints(update_proto.new_linear_constraints());
458  quadratic_constraints_.AddConstraints(
459  update_proto.quadratic_constraint_updates().new_constraints());
460  sos1_constraints_.AddConstraints(
461  update_proto.sos1_constraint_updates().new_constraints());
462  sos2_constraints_.AddConstraints(
463  update_proto.sos2_constraint_updates().new_constraints());
464  indicator_constraints_.AddConstraints(
465  update_proto.indicator_constraint_updates().new_constraints());
466 
467  // Update the objective.
468  if (update_proto.objective_updates().has_direction_update()) {
469  set_is_maximize(update_proto.objective_updates().direction_update());
470  }
471  if (update_proto.objective_updates().has_offset_update()) {
472  set_objective_offset(update_proto.objective_updates().offset_update());
473  }
474  UpdateLinearObjectiveCoefficients(
475  update_proto.objective_updates().linear_coefficients());
476  UpdateQuadraticObjectiveCoefficients(
477  update_proto.objective_updates().quadratic_coefficients());
478 
479  // Update the linear constraints' coefficients.
480  UpdateLinearConstraintCoefficients(
481  update_proto.linear_constraint_matrix_updates());
482 
483  return absl::OkStatus();
484 }
485 
486 } // namespace math_opt
487 } // namespace operations_research
#define RETURN_IF_ERROR(expr)
absl::Status Insert(int64_t id, std::string name)
absl::Status SetNextFreeId(int64_t new_next_free_id)
void DeleteVariable(VariableId variable, const iterator_range< DiffIter > &diffs)
void Delete(LinearConstraintId id, const iterator_range< DiffIter > &diffs)
UpdateResult Update(const Diff &diff, const absl::flat_hash_set< VariableId > &deleted_variables, const std::vector< VariableId > &new_variables) const
LinearConstraintId Add(double lower_bound, double upper_bound, absl::string_view name)
std::vector< LinearConstraintId > LinearConstraints() const
std::pair< LinearConstraintsProto, SparseDoubleMatrixProto > Proto() const
std::vector< LinearConstraintId > SortedLinearConstraints() const
LinearConstraintId next_linear_constraint_id() const
void set_quadratic_objective_coefficient(VariableId first_variable, VariableId second_variable, double value)
std::vector< VariableId > SortedVariables() const
static absl::StatusOr< std::unique_ptr< ModelStorage > > FromModelProto(const ModelProto &model_proto)
std::optional< ModelUpdateProto > ExportModelUpdate(UpdateTrackerId update_tracker) const
void DeleteLinearConstraint(LinearConstraintId id)
void set_linear_objective_coefficient(VariableId variable, double value)
const AtomicConstraintTraits< IdType >::ConstraintData & constraint_data(IdType id) const
void DeleteUpdateTracker(UpdateTrackerId update_tracker)
std::vector< VariableId > variables() const
absl::Status ApplyUpdateProto(const ModelUpdateProto &update_proto)
void set_linear_constraint_coefficient(LinearConstraintId constraint, VariableId variable, double value)
VariableId AddVariable(absl::string_view name="")
void AdvanceCheckpoint(UpdateTrackerId update_tracker)
void set_variable_upper_bound(VariableId id, double upper_bound)
void set_variable_is_integer(VariableId id, bool is_integer)
void ensure_next_variable_id_at_least(VariableId id)
const std::string & variable_name(VariableId id) const
void set_linear_constraint_upper_bound(LinearConstraintId id, double upper_bound)
void set_linear_constraint_lower_bound(LinearConstraintId id, double lower_bound)
std::vector< LinearConstraintId > LinearConstraints() const
std::unique_ptr< ModelStorage > Clone(std::optional< absl::string_view > new_name=std::nullopt) const
void ensure_next_linear_constraint_id_at_least(LinearConstraintId id)
const std::string & linear_constraint_name(LinearConstraintId id) const
LinearConstraintId AddLinearConstraint(absl::string_view name="")
std::vector< LinearConstraintId > SortedLinearConstraints() const
void set_variable_lower_bound(VariableId id, double lower_bound)
void DeleteVariable(VariableId variable, const iterator_range< DiffIter > &diffs)
ObjectiveUpdatesProto Update(const Diff &diff, const absl::flat_hash_set< VariableId > &deleted_variables, const std::vector< VariableId > &new_variables) const
Data & GetData(UpdateTrackerId update_tracker)
void DeleteUpdateTracker(UpdateTrackerId update_tracker)
const std::vector< IdDataPair > & GetUpdatedTrackers()
UpdateTrackerId NewUpdateTracker(T &&... args)
void Delete(VariableId id, const iterator_range< DiffIter > &diffs)
std::vector< VariableId > SortedVariables() const
std::vector< VariableId > VariablesFrom(VariableId start) const
VariableId Add(double lower_bound, double upper_bound, bool is_integer, absl::string_view name)
std::vector< VariableId > Variables() const
UpdateResult Update(const Diff &diff) const
CpModelProto const * model_proto
const std::string name
int64_t value
absl::Status status
Definition: g_gurobi.cc:41
absl::Span< const double > coefficients
absl::Status ValidateModelUpdate(const ModelUpdateProto &model_update, ModelSummary &model_summary)
absl::StatusOr< ModelSummary > ValidateModel(const ModelProto &model, const bool check_names)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
Collection of objects used to extend the Constraint Solver library.
IntVar * upper_bound
Definition: routing.cc:1087
IntVar * lower_bound
Definition: routing.cc:1086