OR-Tools  9.6
linear_constraint_storage.h
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 
14 #ifndef OR_TOOLS_MATH_OPT_STORAGE_LINEAR_CONSTRAINT_STORAGE_H_
15 #define OR_TOOLS_MATH_OPT_STORAGE_LINEAR_CONSTRAINT_STORAGE_H_
16 
17 #include <algorithm>
18 #include <cstdint>
19 #include <limits>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include "absl/container/flat_hash_map.h"
25 #include "absl/container/flat_hash_set.h"
26 #include "absl/meta/type_traits.h"
27 #include "absl/strings/string_view.h"
28 #include "absl/log/check.h"
30 #include "ortools/math_opt/model.pb.h"
31 #include "ortools/math_opt/model_update.pb.h"
32 #include "ortools/math_opt/sparse_containers.pb.h"
36 
38 
39 // In memory representation of the linear constraints of an optimization model.
40 //
41 // The setter functions all accept a `DiffIter`, which must be an iterator over
42 // non-const references to LinearConstraintStorage::Diff. These
43 // functions will modify the LinearConstraintStorage::Diff objects.
45  public:
46  // Tracks a "checkpoint" and changes to linear constraints that are before the
47  // checkpoint. Advancing the checkpoint throws away tracked changes.
48  //
49  // An instance of this class is owned by each update tracker of ModelStorage.
50  struct Diff {
51  // Note: no reference to storage is held.
52  Diff(const LinearConstraintStorage& storage,
53  VariableId variable_checkpoint);
54 
55  LinearConstraintId checkpoint{0};
56  VariableId variable_checkpoint{0};
57  absl::flat_hash_set<LinearConstraintId> deleted;
58  absl::flat_hash_set<LinearConstraintId> lower_bounds;
59  absl::flat_hash_set<LinearConstraintId> upper_bounds;
60  // Only for pairs where both the variable and constraint are before the
61  // checkpoint, i.e.
62  // var_id < variables_checkpoint_ &&
63  // lin_con_id < linear_constraints_checkpoint_
64  absl::flat_hash_set<std::pair<LinearConstraintId, VariableId>> matrix_keys;
65  };
66 
67  struct UpdateResult {
68  google::protobuf::RepeatedField<int64_t> deleted;
69  LinearConstraintUpdatesProto updates;
70  LinearConstraintsProto creates;
71  SparseDoubleMatrixProto matrix_updates;
72  };
73 
74  // Adds a linear constraint to the model and returns its id.
75  //
76  // The returned ids begin at zero and strictly increase (in particular, if
77  // ensure_next_id_at_least() is not used, they will be consecutive). Deleted
78  // ids are NOT reused.
79  LinearConstraintId Add(double lower_bound, double upper_bound,
80  absl::string_view name);
81 
82  inline double lower_bound(LinearConstraintId id) const;
83  inline double upper_bound(LinearConstraintId id) const;
84  inline const std::string& name(LinearConstraintId id) const;
85 
86  template <typename DiffIter>
87  void set_lower_bound(LinearConstraintId id, double lower_bound,
88  const iterator_range<DiffIter>& diffs);
89 
90  template <typename DiffIter>
91  void set_upper_bound(LinearConstraintId id, double upper_bound,
92  const iterator_range<DiffIter>& diffs);
93 
94  // Removes a linear constraint from the model.
95  //
96  // It is an error to use a deleted linear constraint id as input to any
97  // subsequent function calls on the model.
98  template <typename DiffIter>
99  void Delete(LinearConstraintId id, const iterator_range<DiffIter>& diffs);
100 
101  // The number of linear constraints in the model.
102  //
103  // Equal to the number of linear constraints created minus the number of
104  // linear constraints deleted.
105  inline int64_t size() const;
106 
107  // The returned id of the next call to AddLinearConstraint.
108  //
109  // Equal to the number of linear constraints created.
110  inline LinearConstraintId next_id() const;
111 
112  // Sets the next variable id to be the maximum of next_id() and `minimum`.
113  inline void ensure_next_id_at_least(LinearConstraintId minimum);
114 
115  // Returns true if this id has been created and not yet deleted.
116  inline bool contains(LinearConstraintId id) const;
117 
118  // The LinearConstraintsIds in use (not deleted), order not defined.
119  std::vector<LinearConstraintId> LinearConstraints() const;
120 
121  // Returns a sorted vector of all existing (not deleted) linear constraints in
122  // the model.
123  //
124  // Runs in O(n log(n)), where n is the number of linear constraints returned.
125  std::vector<LinearConstraintId> SortedLinearConstraints() const;
126 
127  // Removes all occurrences of var from the constraint matrix.
128  template <typename DiffIter>
129  void DeleteVariable(VariableId variable,
130  const iterator_range<DiffIter>& diffs);
131 
132  // Setting value=0 deletes the key from the matrix.
133  template <typename DiffIter>
134  void set_term(LinearConstraintId constraint, VariableId variable,
135  double value, const iterator_range<DiffIter>& diffs);
136 
137  // The matrix of coefficients for the linear terms in the constraints.
139  return matrix_;
140  }
141 
142  // Returns an equivalent proto of `this`.
143  std::pair<LinearConstraintsProto, SparseDoubleMatrixProto> Proto() const;
144 
146  // Functions for working with Diff
148 
149  // Returns true if there are no changes (tracked changes before the checkpoint
150  // or new constraints after the checkpoint).
151  //
152  // NOTE: when a linear constraint coefficient is modified for a variable past
153  // the checkpoint, the Diff object can be empty (and diff_is_empty will return
154  // true), but Update() can return a non-empty UpdateResult. This behavior
155  // MAY CHANGE in the future, so diff_is_empty is true iff the UpdateResult
156  // returned by Update() is empty (a more intuitive API, harder to implement
157  // efficiently).
158  inline bool diff_is_empty(const Diff& diff) const;
159 
160  UpdateResult Update(const Diff& diff,
161  const absl::flat_hash_set<VariableId>& deleted_variables,
162  const std::vector<VariableId>& new_variables) const;
163 
164  // Updates the checkpoint and clears all stored changes in diff.
165  void AdvanceCheckpointInDiff(VariableId variable_checkpoint,
166  Diff& diff) const;
167 
168  private:
169  struct Data {
170  double lower_bound = -std::numeric_limits<double>::infinity();
171  double upper_bound = std::numeric_limits<double>::infinity();
172  std::string name;
173  };
174 
175  std::vector<LinearConstraintId> ConstraintsFrom(
176  const LinearConstraintId start) const;
177 
178  void AppendConstraint(LinearConstraintId constraint,
179  LinearConstraintsProto* proto) const;
180 
181  // Returns a proto representation of the constraints with id in [start, end).
182  // (Note: the linear coefficients must be queried separately).
183  LinearConstraintsProto Proto(LinearConstraintId start,
184  LinearConstraintId end) const;
185 
186  LinearConstraintId next_id_{0};
187  absl::flat_hash_map<LinearConstraintId, Data> linear_constraints_;
188  SparseMatrix<LinearConstraintId, VariableId> matrix_;
189 };
190 
192 // Inline function implementation
194 
195 double LinearConstraintStorage::lower_bound(const LinearConstraintId id) const {
196  return linear_constraints_.at(id).lower_bound;
197 }
198 
199 double LinearConstraintStorage::upper_bound(const LinearConstraintId id) const {
200  return linear_constraints_.at(id).upper_bound;
201 }
202 
203 const std::string& LinearConstraintStorage::name(
204  const LinearConstraintId id) const {
205  return linear_constraints_.at(id).name;
206 }
207 
208 template <typename DiffIter>
210  const LinearConstraintId id, const double lower_bound,
211  const iterator_range<DiffIter>& diffs) {
212  const auto it = linear_constraints_.find(id);
213  if (it->second.lower_bound == lower_bound) {
214  return;
215  }
216  it->second.lower_bound = lower_bound;
217  for (Diff& diff : diffs) {
218  if (id < diff.checkpoint) {
219  diff.lower_bounds.insert(id);
220  }
221  }
222 }
223 
224 template <typename DiffIter>
226  const LinearConstraintId id, const double upper_bound,
227  const iterator_range<DiffIter>& diffs) {
228  const auto it = linear_constraints_.find(id);
229  if (it->second.upper_bound == upper_bound) {
230  return;
231  }
232  it->second.upper_bound = upper_bound;
233  for (Diff& diff : diffs) {
234  if (id < diff.checkpoint) {
235  diff.upper_bounds.insert(id);
236  }
237  }
238 }
239 
240 template <typename DiffIter>
241 void LinearConstraintStorage::Delete(const LinearConstraintId id,
242  const iterator_range<DiffIter>& diffs) {
243  for (Diff& diff : diffs) {
244  // if the constraint >= checkpoint_, we don't store any info.
245  if (id >= diff.checkpoint) {
246  continue;
247  }
248  diff.lower_bounds.erase(id);
249  diff.upper_bounds.erase(id);
250  diff.deleted.insert(id);
251  for (const VariableId row_var : matrix_.row(id)) {
252  if (row_var < diff.variable_checkpoint) {
253  diff.matrix_keys.erase({id, row_var});
254  }
255  }
256  }
257  matrix_.DeleteRow(id);
258  linear_constraints_.erase(id);
259 }
260 
261 template <typename DiffIter>
263  const VariableId variable, const iterator_range<DiffIter>& diffs) {
264  for (Diff& diff : diffs) {
265  if (variable >= diff.variable_checkpoint) {
266  continue;
267  }
268  for (const LinearConstraintId constraint : matrix_.column(variable)) {
269  if (constraint < diff.checkpoint) {
270  diff.matrix_keys.erase({constraint, variable});
271  }
272  }
273  }
274  matrix_.DeleteColumn(variable);
275 }
276 
278  return linear_constraints_.size();
279 }
280 
281 LinearConstraintId LinearConstraintStorage::next_id() const { return next_id_; }
282 
284  const LinearConstraintId minimum) {
285  next_id_ = std::max(minimum, next_id_);
286 }
287 
288 bool LinearConstraintStorage::contains(const LinearConstraintId id) const {
289  return linear_constraints_.contains(id);
290 }
291 
292 template <typename DiffIter>
293 void LinearConstraintStorage::set_term(const LinearConstraintId constraint,
294  const VariableId variable,
295  const double value,
296  const iterator_range<DiffIter>& diffs) {
297  DCHECK(linear_constraints_.contains(constraint));
298  if (!matrix_.set(constraint, variable, value)) {
299  return;
300  }
301  for (Diff& diff : diffs) {
302  if (constraint < diff.checkpoint && variable < diff.variable_checkpoint) {
303  diff.matrix_keys.insert({constraint, variable});
304  }
305  }
306 }
307 
309  return next_id_ <= diff.checkpoint && diff.deleted.empty() &&
310  diff.lower_bounds.empty() && diff.upper_bounds.empty() &&
311  diff.matrix_keys.empty();
312 }
313 
314 } // namespace operations_research::math_opt
315 
316 #endif // OR_TOOLS_MATH_OPT_STORAGE_LINEAR_CONSTRAINT_STORAGE_H_
int64_t max
Definition: alldiff_cst.cc:140
void set_upper_bound(LinearConstraintId id, double upper_bound, const iterator_range< DiffIter > &diffs)
void DeleteVariable(VariableId variable, const iterator_range< DiffIter > &diffs)
const SparseMatrix< LinearConstraintId, VariableId > & matrix() const
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
void set_lower_bound(LinearConstraintId id, double lower_bound, const iterator_range< DiffIter > &diffs)
void AdvanceCheckpointInDiff(VariableId variable_checkpoint, Diff &diff) const
const std::string & name(LinearConstraintId id) const
LinearConstraintId Add(double lower_bound, double upper_bound, absl::string_view name)
void set_term(LinearConstraintId constraint, VariableId variable, double value, const iterator_range< DiffIter > &diffs)
std::vector< LinearConstraintId > LinearConstraints() const
std::pair< LinearConstraintsProto, SparseDoubleMatrixProto > Proto() const
std::vector< LinearConstraintId > SortedLinearConstraints() const
bool set(RowId row, ColumnId column, double value)
std::vector< RowId > column(ColumnId column_id) const
std::vector< ColumnId > row(RowId row_id) const
CpModelProto proto
int64_t value
IntVar * upper_bound
Definition: routing.cc:1087
IntVar * lower_bound
Definition: routing.cc:1086
std::optional< int64_t > end
int64_t start
absl::flat_hash_set< std::pair< LinearConstraintId, VariableId > > matrix_keys
Diff(const LinearConstraintStorage &storage, VariableId variable_checkpoint)