Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
rev.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// Reversible (i.e Backtrackable) classes, used to simplify coding propagators.
15#ifndef OR_TOOLS_UTIL_REV_H_
16#define OR_TOOLS_UTIL_REV_H_
17
18#include <cstdint>
19#include <utility>
20#include <vector>
21
22#include "absl/container/flat_hash_map.h"
25
26namespace operations_research {
27
28// Interface for reversible objects used to maintain them in sync with a tree
29// search organized by decision levels.
31 public:
34
35 // Initially a reversible class starts at level zero. Increasing the level
36 // saves the state of the current old level. Decreasing the level restores the
37 // state to what it was at this level and all higher levels are forgotten.
38 // Everything done at level zero cannot be backtracked over.
39 //
40 // The level is assumed to be non-negative.
41 virtual void SetLevel(int level) = 0;
42};
43
44// A repository that maintains a set of reversible objects of type T.
45// This is meant to be used for small types that are efficient to copy, like
46// all the basic types, std::pair and things like this.
47template <class T>
49 public:
50 RevRepository() : stamp_(0) {}
51
52 // This works in O(level_diff) on level increase.
53 // For level decrease, it is in O(level_diff + num_restored_states).
54 void SetLevel(int level) final;
55 int Level() const { return end_of_level_.size(); }
56
57 // Saves the given object value for the current level. If this is called
58 // multiple time by level, only the value of the first call matter. This is
59 // NOT optimized for many calls by level and should mainly be used just once
60 // for a given level. If a client cannot do that efficiently, it can use the
61 // SaveStateWithStamp() function below.
62 void SaveState(T* object) {
63 if (end_of_level_.empty()) return; // Not useful for level zero.
64 stack_.push_back({object, *object});
65 }
66
67 // Calls SaveState() if the given stamp is not the same as the current one.
68 // This also sets the given stamp to the current one. The current stamp is
69 // maintained by this class and is updated on each level changes. The whole
70 // process make sure that only one SaveValue() par level will ever be called,
71 // so it is efficient to call this before each update to the object T.
72 void SaveStateWithStamp(T* object, int64_t* stamp) {
73 if (*stamp == stamp_) return;
74 *stamp = stamp_;
75 SaveState(object);
76 }
77
78 private:
79 int64_t stamp_;
80 std::vector<int> end_of_level_; // In stack_.
81
82 // TODO(user): If we ever see this in any cpu profile, consider using two
83 // vectors for a better memory packing in case sizeof(T) is not sizeof(T*).
84 std::vector<std::pair<T*, T>> stack_;
85};
86
87// A basic reversible vector implementation.
88template <class IndexType, class T>
90 public:
91 const T& operator[](IndexType index) const { return vector_[index]; }
92
93 // TODO(user): Maybe we could have also used the [] operator, but it is harder
94 // to be 100% sure that the mutable version is only called when we modify
95 // the vector. And I had performance bug because of that.
96 T& MutableRef(IndexType index) {
97 // Save on the stack first.
98 if (!end_of_level_.empty()) stack_.push_back({index, vector_[index]});
99 return vector_[index];
100 }
101
102 int size() const { return vector_.size(); }
103
104 void Grow(int new_size) {
105 CHECK_GE(new_size, vector_.size());
106 vector_.resize(new_size);
107 }
108
109 void GrowByOne() { vector_.resize(vector_.size() + 1); }
110
111 int Level() const { return end_of_level_.size(); }
112
113 void SetLevel(int level) final {
114 DCHECK_GE(level, 0);
115 if (level == Level()) return;
116 if (level < Level()) {
117 const int index = end_of_level_[level];
118 end_of_level_.resize(level); // Shrinks.
119 for (int i = stack_.size() - 1; i >= index; --i) {
120 vector_[stack_[i].first] = stack_[i].second;
121 }
122 stack_.resize(index);
123 } else {
124 end_of_level_.resize(level, stack_.size()); // Grows.
125 }
126 }
127
128 private:
129 std::vector<int> end_of_level_; // In stack_.
130 std::vector<std::pair<IndexType, T>> stack_;
132};
133
134template <class T>
136 DCHECK_GE(level, 0);
137 if (level == Level()) return;
138 ++stamp_;
139 if (level < Level()) {
140 const int index = end_of_level_[level];
141 end_of_level_.resize(level); // Shrinks.
142 for (int i = stack_.size() - 1; i >= index; --i) {
143 *stack_[i].first = stack_[i].second;
144 }
145 stack_.resize(index);
146 } else {
147 end_of_level_.resize(level, stack_.size()); // Grows.
148 }
149}
150
151// Like a normal map but support backtrackable operations.
152//
153// This works on any class "Map" that supports: begin(), end(), find(), erase(),
154// insert(), key_type, value_type, mapped_type and const_iterator.
155template <class Map>
157 public:
158 typedef typename Map::key_type key_type;
159 typedef typename Map::mapped_type mapped_type;
160 typedef typename Map::value_type value_type;
161 typedef typename Map::const_iterator const_iterator;
162
163 // Backtracking support: changes the current "level" (always non-negative).
164 //
165 // Initially the class starts at level zero. Increasing the level works in
166 // O(level diff) and saves the state of the current old level. Decreasing the
167 // level restores the state to what it was at this level and all higher levels
168 // are forgotten. Everything done at level zero cannot be backtracked over.
169 void SetLevel(int level) final;
170 int Level() const { return first_op_index_of_next_level_.size(); }
171
172 bool contains(key_type key) const { return map_.contains(key); }
173 const mapped_type& at(key_type key) const { return map_.at(key); }
174
175 void EraseOrDie(key_type key);
176 void Set(key_type key, mapped_type value); // Adds or overwrites.
177
178 // Wrapper to the underlying const map functions.
179 int size() const { return map_.size(); }
180 bool empty() const { return map_.empty(); }
181 const_iterator find(const key_type& k) const { return map_.find(k); }
182 const_iterator begin() const { return map_.begin(); }
183 const_iterator end() const { return map_.end(); }
184
185 private:
186 Map map_;
187
188 // The operation that needs to be performed to reverse one modification:
189 // - If is_deletion is true, then we need to delete the entry with given key.
190 // - Otherwise we need to add back (or overwrite) the saved entry.
191 struct UndoOperation {
192 bool is_deletion;
193 key_type key;
194 mapped_type value;
195 };
196
197 // TODO(user): We could merge the operations with the same key from the same
198 // level. Investigate and implement if this is worth the effort for our use
199 // case.
200 std::vector<UndoOperation> operations_;
201 std::vector<int> first_op_index_of_next_level_;
202};
203
204template <class Map>
205void RevMap<Map>::SetLevel(int level) {
206 DCHECK_GE(level, 0);
207 if (level < Level()) {
208 const int backtrack_level = first_op_index_of_next_level_[level];
209 first_op_index_of_next_level_.resize(level); // Shrinks.
210 while (operations_.size() > backtrack_level) {
211 const UndoOperation& to_undo = operations_.back();
212 if (to_undo.is_deletion) {
213 map_.erase(to_undo.key);
214 } else {
215 map_.insert({to_undo.key, to_undo.value}).first->second = to_undo.value;
216 }
217 operations_.pop_back();
218 }
219 return;
220 }
221
222 // This is ok even if level == Level().
223 first_op_index_of_next_level_.resize(level, operations_.size()); // Grows.
224}
225
226template <class Map>
228 const auto iter = map_.find(key);
229 if (iter == map_.end()) LOG(FATAL) << "key not present: '" << key << "'.";
230 if (Level() > 0) {
231 operations_.push_back({false, key, iter->second});
232 }
233 map_.erase(iter);
234}
235
236template <class Map>
238 auto insertion_result = map_.insert({key, value});
239 if (Level() > 0) {
240 if (insertion_result.second) {
241 // It is an insertion. Undo = delete.
242 operations_.push_back({true, key});
243 } else {
244 // It is a modification. Undo = change back to old value.
245 operations_.push_back({false, key, insertion_result.first->second});
246 }
247 }
248 insertion_result.first->second = value;
249}
250
251// A basic backtrackable multi map that can only grow (except on backtrack).
252template <class Key, class Value>
254 public:
255 void SetLevel(int level) final;
256
257 // Adds a new value at the given key.
258 void Add(Key key, Value value);
259
260 // Returns the list of values for a given key (can be empty).
261 const std::vector<Value>& Values(Key key) const;
262
263 private:
264 std::vector<Value> empty_values_;
265
266 // TODO(user): use inlined vectors. Another datastructure that may be more
267 // efficient is to use a linked list inside added_keys_ for the values sharing
268 // the same key.
269 absl::flat_hash_map<Key, std::vector<Value>> map_;
270
271 // Backtracking data.
272 std::vector<Key> added_keys_;
273 std::vector<int> first_added_key_of_next_level_;
274};
275
276template <class Key, class Value>
278 DCHECK_GE(level, 0);
279 if (level < first_added_key_of_next_level_.size()) {
280 const int backtrack_level = first_added_key_of_next_level_[level];
281 first_added_key_of_next_level_.resize(level); // Shrinks.
282 while (added_keys_.size() > backtrack_level) {
283 auto it = map_.find(added_keys_.back());
284 if (it->second.size() > 1) {
285 it->second.pop_back();
286 } else {
287 map_.erase(it);
288 }
289 added_keys_.pop_back();
290 }
291 return;
292 }
293
294 // This is ok even if level == Level().
295 first_added_key_of_next_level_.resize(level, added_keys_.size()); // Grows.
296}
297
298template <class Key, class Value>
300 Key key) const {
301 const auto it = map_.find(key);
302 if (it != map_.end()) return it->second;
303 return empty_values_;
304}
305
306template <class Key, class Value>
308 if (!first_added_key_of_next_level_.empty()) {
309 added_keys_.push_back(key);
310 }
311 map_[key].push_back(value);
312}
313
314} // namespace operations_research
315
316#endif // OR_TOOLS_UTIL_REV_H_
A basic backtrackable multi map that can only grow (except on backtrack).
Definition rev.h:253
void SetLevel(int level) final
Definition rev.h:277
void Add(Key key, Value value)
Adds a new value at the given key.
Definition rev.h:307
const std::vector< Value > & Values(Key key) const
Returns the list of values for a given key (can be empty).
Definition rev.h:299
void EraseOrDie(key_type key)
Definition rev.h:227
bool empty() const
Definition rev.h:180
Map::value_type value_type
Definition rev.h:160
bool contains(key_type key) const
Definition rev.h:172
Map::key_type key_type
Definition rev.h:158
const_iterator end() const
Definition rev.h:183
int size() const
Wrapper to the underlying const map functions.
Definition rev.h:179
const mapped_type & at(key_type key) const
Definition rev.h:173
const_iterator begin() const
Definition rev.h:182
const_iterator find(const key_type &k) const
Definition rev.h:181
Map::mapped_type mapped_type
Definition rev.h:159
void SetLevel(int level) final
Definition rev.h:205
Map::const_iterator const_iterator
Definition rev.h:161
void Set(key_type key, mapped_type value)
Definition rev.h:237
void SetLevel(int level) final
Definition rev.h:135
void SaveStateWithStamp(T *object, int64_t *stamp)
Definition rev.h:72
void SaveState(T *object)
Definition rev.h:62
A basic reversible vector implementation.
Definition rev.h:89
void Grow(int new_size)
Definition rev.h:104
const T & operator[](IndexType index) const
Definition rev.h:91
T & MutableRef(IndexType index)
Definition rev.h:96
void SetLevel(int level) final
Definition rev.h:113
virtual void SetLevel(int level)=0
void resize(size_type new_size)
int64_t value
int index
In SWIG mode, we don't want anything besides these top-level includes.
int64_t stamp
Definition search.cc:3270