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