OR-Tools  9.6
atomic_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_ATOMIC_CONSTRAINT_STORAGE_H_
15 #define OR_TOOLS_MATH_OPT_STORAGE_ATOMIC_CONSTRAINT_STORAGE_H_
16 
17 #include <algorithm>
18 #include <cstdint>
19 #include <vector>
20 
21 #include "absl/algorithm/container.h"
22 #include "absl/container/flat_hash_map.h"
23 #include "absl/container/flat_hash_set.h"
24 #include "google/protobuf/map.h"
25 #include "absl/log/check.h"
26 #include "ortools/base/map_util.h"
31 
33 
34 // Storage for a "mapped" constraint type whose only supported updates are
35 // constraint addition, and variable or constraint deletion.
36 //
37 // The constraints are "atomic" in the sense that they can be added or deleted
38 // individually, but direct data updates (e.g., to coefficients) are not
39 // permitted. Note that they are strictly immutable, though, as variable
40 // deletions may have side effects (e.g., a constraint considers a deleted
41 // variable as implicitly fixed to zero).
42 //
43 // Implementers of new constraint families should provide a conforming
44 // `ConstraintData` definition, along with a template specialization of
45 // `AtomicConstraintTraits` (defined below). These should likely be placed in
46 // `math_opt/constraints/$new_constraint_family/storage.h`.
47 //
48 // The `ConstraintData` parameter should refer to a class to represent a single
49 // constraint in memory. It must satisfy a duck-typed interface:
50 // * A `IdType` alias to the (strong int) ID type for the constraint class.
51 // * A `ProtoType` alias to the proto message for a single constraint.
52 // * A `UpdatesProtoType` alias to the proto message for updates for the given
53 // constraint type, as represented by two fields:
54 // - repeated int64_t deleted_constraint_ids
55 // - Map<int64_t, ProtoType> new_constraints
56 // * A member function to return all variables involved in the constraint:
57 // std::vector<VariableId> RelatedVariables() const;
58 // * A member function to delete a variable from the constraint:
59 // void DeleteVariable(VariableId var);
60 // * A member function that returns a proto representation of the constraint:
61 // ProtoType Proto() const;
62 // * A static member function that initializes a constraint from its proto
63 // representation:
64 // static ConstraintData FromProto(const ProtoType& in_proto);
65 //
66 // The setter functions all accept a `DiffIter`, which must be an iterator over
67 // non-const references to AtomicConstraintStorage<ConstraintData>::Diff. These
68 // functions will modify the diff objects.
69 template <typename ConstraintData>
71  public:
72  using IdType = typename ConstraintData::IdType;
73  using ProtoType = typename ConstraintData::ProtoType;
74  using UpdatesProtoType = typename ConstraintData::UpdatesProtoType;
75 
76  // Tracks a "checkpoint" and changes to constraints of a given class that are
77  // before the checkpoint. Advancing the checkpoint throws away tracked
78  // changes.
79  //
80  // An instance of this class is owned by each update tracker of ModelStorage.
81  struct Diff {
83  : checkpoint(storage.next_id()) {}
84 
86  absl::flat_hash_set<IdType> deleted_constraints;
87  };
88 
89  // Add a single constraint to the storage.
90  IdType AddConstraint(ConstraintData constraint);
91 
92  // Adds a collection of constraints to the storage, from an "id-to-proto" map.
93  // The keys for the input map will be used as the associated IDs in storage.
94  //
95  // Will CHECK-fail if any ID is less than next_id().
97  const google::protobuf::Map<int64_t, ProtoType>& constraints);
98 
99  // Delete a single constraint.
100  template <typename DiffIter>
101  void Delete(IdType id, const iterator_range<DiffIter>& diffs);
102 
103  // Delete a single variable from each constraint in the storage.
104  void DeleteVariable(VariableId variable_id);
105 
106  // The number of constraints stored (includes everything created and not yet
107  // deleted).
108  int64_t size() const { return constraint_data_.size(); }
109 
110  // The smallest ID which is valid for a new constraint.
111  IdType next_id() const { return next_id_; }
112 
113  // Sets the next variable id to be the maximum of next_id() and `minimum`.
114  void ensure_next_id_at_least(const IdType minimum) {
115  next_id_ = std::max(minimum, next_id_);
116  }
117 
118  // Returns true if this id has been created and not yet deleted.
119  bool contains(const IdType id) const { return constraint_data_.contains(id); }
120 
121  const absl::flat_hash_set<IdType>& RelatedConstraints(
122  const VariableId variable_id) const {
123  return gtl::FindWithDefault(constraints_by_variable_, variable_id);
124  }
125 
126  // The ids in use (not deleted). The order is not defined.
127  std::vector<IdType> Constraints() const;
128 
129  // Returns a sorted vector of all existing (not deleted) constraints in the
130  // model.
131  //
132  // Runs in O(n log(n)), where n is the number of constraints returned.
133  std::vector<IdType> SortedConstraints() const;
134 
135  // Returns a proto representation of the constraint class.
136  google::protobuf::Map<int64_t, ProtoType> Proto() const {
137  google::protobuf::Map<int64_t, ProtoType> constraints;
138  for (const auto& [id, data] : constraint_data_) {
139  constraints[id.value()] = data.Proto();
140  }
141  return constraints;
142  }
143 
144  // Returns the underlying data for constraint `id`.
145  // Will crash if `id` is not present (i.e., if `contains(id)` returns false).
146  const ConstraintData& data(const IdType id) const {
147  return constraint_data_.at(id);
148  }
149 
151  // Functions for working with Diff
153 
154  // Returns true if there are no changes (tracked changes before the checkpoint
155  // or new constraints after the checkpoint).
156  inline bool diff_is_empty(const Diff& diff) const;
157 
158  // Return a proto representation of the current update.
159  typename ConstraintData::UpdatesProtoType Update(const Diff& diff) const;
160 
161  // Updates the checkpoint and clears all stored changes in diff.
162  void AdvanceCheckpointInDiff(Diff& diff) const;
163 
164  private:
165  IdType next_id_{0};
166  absl::flat_hash_map<IdType, ConstraintData> constraint_data_;
167  // TODO(b/232619901): Use std::vector<IdType> as values and lazily compact it.
168  absl::flat_hash_map<VariableId, absl::flat_hash_set<IdType>>
169  constraints_by_variable_;
170 };
171 
172 // A placeholder templated definition for traits-based parameter inference when
173 // working with atomic constraint families.
174 //
175 // For template specializations on `IdType`: the `ConstraintData` typedef must
176 // satisfy IdType == AtomicConstraintStorage<ConstraintData>::IdType.
177 template <typename IdType>
179  using ConstraintData = void;
180 };
181 
183 // Inlined implementations
185 
186 template <typename ConstraintData>
189  ConstraintData constraint) {
190  const std::vector<VariableId> vars = constraint.RelatedVariables();
191  const IdType id = next_id_++;
192  CHECK(constraint_data_.insert({id, std::move(constraint)}).second);
193  for (const VariableId v : vars) {
194  constraints_by_variable_[v].insert(id);
195  }
196  return id;
197 }
198 
199 template <typename ConstraintData>
201  const google::protobuf::Map<int64_t, ProtoType>& constraints) {
202  for (const int64_t raw_id : SortedMapKeys(constraints)) {
203  const IdType id(raw_id);
204  CHECK_GE(id, next_id())
205  << "constraint ID in map: " << raw_id
206  << " is smaller than next_id(): " << next_id().value();
207  ensure_next_id_at_least(id);
208  AddConstraint(ConstraintData::FromProto(constraints.at(raw_id)));
209  }
210 }
211 
212 template <typename ConstraintData>
213 template <typename DiffIter>
215  IdType id, const iterator_range<DiffIter>& diffs) {
216  for (Diff& diff : diffs) {
217  // if the constraint >= checkpoint_, we don't store any info.
218  if (id >= diff.checkpoint) {
219  continue;
220  }
221  diff.deleted_constraints.insert(id);
222  }
223  const auto data = constraint_data_.find(id);
224  CHECK(data != constraint_data_.end());
225  for (const VariableId v : data->second.RelatedVariables()) {
226  constraints_by_variable_[v].erase(id);
227  }
228  constraint_data_.erase(id);
229 }
230 
231 template <typename ConstraintData>
233  VariableId variable_id) {
234  const auto it = constraints_by_variable_.find(variable_id);
235  if (it == constraints_by_variable_.end()) {
236  return;
237  }
238  for (const IdType constraint_id : it->second) {
239  constraint_data_.at(constraint_id).DeleteVariable(variable_id);
240  }
241  constraints_by_variable_.erase(it);
242 }
243 
244 template <typename ConstraintData>
245 std::vector<typename AtomicConstraintStorage<ConstraintData>::IdType>
247  std::vector<IdType> result;
248  for (const auto& [id, _] : constraint_data_) {
249  result.push_back(id);
250  }
251  return result;
252 }
253 
254 template <typename ConstraintData>
255 std::vector<typename AtomicConstraintStorage<ConstraintData>::IdType>
257  std::vector<IdType> result = Constraints();
258  absl::c_sort(result);
259  return result;
260 }
261 
262 template <typename ConstraintData>
264  const Diff& diff) const {
265  return next_id_ <= diff.checkpoint && diff.deleted_constraints.empty();
266 }
267 
268 template <typename ConstraintData>
270  Diff& diff) const {
271  diff.checkpoint = std::max(diff.checkpoint, next_id_);
272  diff.deleted_constraints.clear();
273 }
274 
275 template <typename ConstraintData>
276 typename ConstraintData::UpdatesProtoType
278  UpdatesProtoType update;
279  for (const IdType deleted_id : diff.deleted_constraints) {
280  update.mutable_deleted_constraint_ids()->Add(deleted_id.value());
281  }
282  absl::c_sort(*update.mutable_deleted_constraint_ids());
283  for (const IdType id :
285  if (contains(id)) {
286  (*update.mutable_new_constraints())[id.value()] = data(id).Proto();
287  }
288  }
289  return update;
290 }
291 
292 } // namespace operations_research::math_opt
293 
294 #endif // OR_TOOLS_MATH_OPT_STORAGE_ATOMIC_CONSTRAINT_STORAGE_H_
int64_t max
Definition: alldiff_cst.cc:140
google::protobuf::Map< int64_t, ProtoType > Proto() const
void AddConstraints(const google::protobuf::Map< int64_t, ProtoType > &constraints)
typename ConstraintData::UpdatesProtoType UpdatesProtoType
const absl::flat_hash_set< IdType > & RelatedConstraints(const VariableId variable_id) const
ConstraintData::UpdatesProtoType Update(const Diff &diff) const
void Delete(IdType id, const iterator_range< DiffIter > &diffs)
const ConstraintData & data(const IdType id) const
int64_t value
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
std::vector< K > SortedMapKeys(const absl::flat_hash_map< K, V > &in_map)
Definition: sorted.h:55
StrongIntRange< IntType > MakeStrongIntRange(IntType end)
Definition: strong_int.h:410
Diff(const AtomicConstraintStorage< ConstraintData > &storage)