Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
routing_filter_committables.h
Go to the documentation of this file.
1// Copyright 2010-2025 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_CONSTRAINT_SOLVER_ROUTING_FILTER_COMMITTABLES_H_
15#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_FILTER_COMMITTABLES_H_
16
17#include <algorithm>
18#include <cstddef>
19#include <cstdint>
20#include <limits>
21#include <vector>
22
23#include "absl/log/check.h"
24#include "absl/types/span.h"
25#include "ortools/util/bitset.h"
27
28namespace operations_research {
29
30// A vector that allows to revert back to a previously committed state,
31// get the set of changed indices, and get current and committed values.
32template <typename T>
34 public:
35 explicit CommittableValue(const T& value)
36 : current_(value), committed_(value) {}
37
38 const T& Get() const { return current_; }
39 const T& GetCommitted() const { return committed_; }
40
41 void Set(const T& value) { current_ = value; }
42
43 void SetAndCommit(const T& value) {
44 Set(value);
45 Commit();
46 }
47
48 void Revert() { current_ = committed_; }
49
50 void Commit() { committed_ = current_; }
51
52 private:
53 T current_;
54 T committed_;
55};
56
57template <typename T>
59 public:
60 // Makes a vector with initial elements all committed to value.
61 CommittableVector<T>(size_t num_elements, const T& value)
62 : elements_(num_elements, {value, value}), changed_(num_elements) {}
63
64 // Return the size of the vector.
65 size_t Size() const { return elements_.size(); }
66
67 // Returns a copy of the value stored at index in the current state.
68 // Does not return a reference, because the class needs to know when elements
69 // are modified.
70 T Get(size_t index) const {
71 DCHECK_LT(index, elements_.size());
72 return elements_[index].current;
73 }
74
75 // Set the value stored at index in the current state to given value.
76 void Set(size_t index, const T& value) {
77 DCHECK_GE(index, 0);
78 DCHECK_LT(index, elements_.size());
79 changed_.Set(index);
80 elements_[index].current = value;
81 }
82
83 // Changes the values of the vector to those in the last Commit().
84 void Revert() {
85 for (const size_t index : changed_.PositionsSetAtLeastOnce()) {
86 elements_[index].current = elements_[index].committed;
87 }
88 changed_.ClearAll();
89 }
90
91 // Makes the current state committed, clearing all changes.
92 void Commit() {
93 for (const size_t index : changed_.PositionsSetAtLeastOnce()) {
94 elements_[index].committed = elements_[index].current;
95 }
96 changed_.ClearAll();
97 }
98
99 // Sets all elements of this vector to given value, and commits to this state.
100 // Supposes that there are no changes since the last Commit() or Revert().
101 void SetAllAndCommit(const T& value) {
102 DCHECK_EQ(0, changed_.NumberOfSetCallsWithDifferentArguments());
103 elements_.assign(elements_.size(), {value, value});
104 }
105
106 // Returns a copy of the value stored at index in the last committed state.
107 T GetCommitted(size_t index) const {
108 DCHECK_LT(index, elements_.size());
109 return elements_[index].committed;
110 }
111
112 // Return true iff the value at index has been Set() since the last Commit()
113 // or Revert(), even if the current value is the same as the committed value.
114 bool HasChanged(size_t index) const { return changed_[index]; }
115
116 // Returns the set of indices that have been Set() since the last Commit() or
117 // Revert().
118 const std::vector<size_t>& ChangedIndices() const {
119 return changed_.PositionsSetAtLeastOnce();
120 }
121
122 // TODO(user): NotifyReverted(), to tell the class that the changes
123 // have brought the vector back to the committed state. This allows O(1)
124 // Revert(), Commit() and empty changed indices.
125
126 private:
127 struct VersionedElement {
128 T current;
129 T committed;
130 };
131 // Holds current and committed versions of values of this vector.
132 std::vector<VersionedElement> elements_;
133 // Holds indices that were Set() since the last Commit() or Revert().
134 SparseBitset<size_t> changed_;
135};
136
137// This class allows to represent a state of dimension values for all paths of
138// a vehicle routing problem. Values of interest for each path are:
139// - nodes,
140// - cumuls (min/max),
141// - transit times,
142// - sum of transit times since the beginning of the path,
143// - span (min/max).
144//
145// This class can maintain two states at once: a committed state and a current
146// state. The current state can be modified by first describing a path p to be
147// modified with PushNode() and MakePathFromNewNodes(). Then the dimension
148// values of this path can be modified with views returned by MutableXXX()
149// methods.
150//
151// When a set of paths has been modified, the caller can decide to definitely
152// change the committed state to the new state, or to revert to the committed
153// state.
154//
155// Operations are meant to be efficient:
156// - all path modifications, i.e. PushNode(), MakePathFromNewNodes(),
157// MutableXXX(), MutableSpan() operations are O(1).
158// - Revert() is O(num changed paths).
159// - Commit() has two behaviors:
160// - if there are less than max_num_committed_elements_ elements in the
161// committed state, then Commit() is O(num changed paths).
162// - otherwise, Commit() does a compaction of the committed state, in
163// O(num_nodes + num_paths).
164// The amortized cost of Commit(), when taking modifications into account,
165// is O(size of changed paths), because all modifications pay at worst
166// O(1) for its own compaction.
167//
168// Note that this class does not support the semantics associated with its
169// fields names, for instance it does not make sure that cumul_min <= cumul_max.
170// The field names are meant for readability for the user.
171// However, path sizes are enforced: if a path has n nodes, then it has
172// n fields for cumul min/max, n for transit_sums, and max(0, n-1) for transits.
174 public:
175 DimensionValues(int num_paths, int num_nodes)
176 : range_of_path_(num_paths, {.begin = 0, .end = 0}),
177 committed_range_of_path_(num_paths, {.begin = 0, .end = 0}),
178 span_(num_paths, Interval::AllIntegers()),
179 committed_span_(num_paths, Interval::AllIntegers()),
180 vehicle_breaks_(num_paths),
181 committed_vehicle_breaks_(num_paths),
182 changed_paths_(num_paths),
183 max_num_committed_elements_(16 * num_nodes) {
184 nodes_.reserve(max_num_committed_elements_);
185 transit_.reserve(max_num_committed_elements_);
186 travel_.reserve(max_num_committed_elements_);
187 travel_sum_.reserve(max_num_committed_elements_);
188 cumul_.reserve(max_num_committed_elements_);
189 }
190
191 struct Interval {
192 int64_t min;
193 int64_t max;
194 // Tests inequality between intervals.
195 bool operator!=(const Interval& other) const {
196 return min != other.min || max != other.max;
197 }
198 // Tests equality between intervals.
199 bool operator==(const Interval& other) const {
200 return min == other.min && max == other.max;
201 }
202 // Returns true iff the interval is empty.
203 bool IsEmpty() const { return min > max; }
204 // Increases the min to be at least lower_bound,
205 // returns true iff the interval is nonempty.
206 bool IncreaseMin(int64_t lower_bound) {
207 min = std::max(min, lower_bound);
208 return min <= max;
209 }
210 // Decreases the max to be at most upper_bound,
211 // returns true iff the interval is nonempty.
212 bool DecreaseMax(int64_t upper_bound) {
213 max = std::min(max, upper_bound);
214 return min <= max;
215 }
216 // Intersects this interval with the other, returns true iff the interval
217 // is nonempty.
218 bool IntersectWith(const Interval& other) {
219 min = std::max(min, other.min);
220 max = std::min(max, other.max);
221 return min <= max;
222 }
223 // A set addition, with intervals: adds other.min to the min, other.max to
224 // the max, with CapAdd().
225 void Add(const Interval& other) {
226 DCHECK(!IsEmpty());
227 DCHECK(!other.IsEmpty());
228 min = CapAdd(min, other.min);
229 max = CapAdd(max, other.max);
230 }
231 // A set subtraction, with intervals: subtracts other.max from the min,
232 // other.min from the max, with CapSub().
233 void Subtract(const Interval& other) {
234 DCHECK(!IsEmpty());
235 DCHECK(!other.IsEmpty());
236 min = CapSub(min, other.max);
237 max = CapSub(max, other.min);
238 }
239 // Returns an interval containing all integers: {kint64min, kint64max}.
241 return {.min = std::numeric_limits<int64_t>::min(),
242 .max = std::numeric_limits<int64_t>::max()};
243 }
244 };
245
251 bool operator==(const VehicleBreak& other) const {
252 return start == other.start && end == other.end &&
253 duration == other.duration && is_performed == other.is_performed;
254 }
255 };
256
257 // Adds a node to new nodes.
258 void PushNode(int node) { nodes_.push_back(node); }
259
260 // Turns new nodes into a new path, allocating dimension values for it.
261 void MakePathFromNewNodes(int path) {
262 DCHECK_GE(path, 0);
263 DCHECK_LT(path, range_of_path_.size());
264 DCHECK(!changed_paths_[path]);
265 range_of_path_[path] = {.begin = num_current_elements_,
266 .end = nodes_.size()};
267 changed_paths_.Set(path);
268 // Allocate dimension values. We allocate n cells for all dimension values,
269 // even transits, so they can all be indexed by the same range_of_path.
270 transit_.resize(nodes_.size(), Interval::AllIntegers());
271 travel_.resize(nodes_.size(), 0);
272 travel_sum_.resize(nodes_.size(), 0);
273 cumul_.resize(nodes_.size(), Interval::AllIntegers());
274 num_current_elements_ = nodes_.size();
275 span_[path] = Interval::AllIntegers();
276 }
277
278 // Resets all path to empty, in both committed and current state.
279 void Reset() {
280 const int num_paths = range_of_path_.size();
281 range_of_path_.assign(num_paths, {.begin = 0, .end = 0});
282 committed_range_of_path_.assign(num_paths, {.begin = 0, .end = 0});
283 changed_paths_.SparseClearAll();
284 num_current_elements_ = 0;
285 num_committed_elements_ = 0;
286 nodes_.clear();
287 transit_.clear();
288 travel_.clear();
289 travel_sum_.clear();
290 cumul_.clear();
291 committed_span_.assign(num_paths, Interval::AllIntegers());
292 }
293
294 // Clears the changed state, make it point to the committed state.
295 void Revert() {
296 for (const int path : changed_paths_.PositionsSetAtLeastOnce()) {
297 range_of_path_[path] = committed_range_of_path_[path];
298 }
299 changed_paths_.SparseClearAll();
300 num_current_elements_ = num_committed_elements_;
301 nodes_.resize(num_current_elements_);
302 transit_.resize(num_current_elements_);
303 travel_.resize(num_current_elements_);
304 travel_sum_.resize(num_current_elements_);
305 cumul_.resize(num_current_elements_);
306 }
307
308 // Makes the committed state point to the current state.
309 // If the state representation is too large, reclaims memory by compacting
310 // the committed state.
311 void Commit() {
312 for (const int path : changed_paths_.PositionsSetAtLeastOnce()) {
313 committed_range_of_path_[path] = range_of_path_[path];
314 committed_span_[path] = span_[path];
315 committed_vehicle_breaks_[path] = vehicle_breaks_[path];
316 }
317 changed_paths_.SparseClearAll();
318 num_committed_elements_ = num_current_elements_;
319 // If the committed data would take too much space, compact the data:
320 // copy committed data to the end of vectors, erase old data, refresh
321 // indexing (range_of_path_).
322 if (num_current_elements_ <= max_num_committed_elements_) return;
323 temp_nodes_.clear();
324 temp_transit_.clear();
325 temp_travel_.clear();
326 temp_travel_sum_.clear();
327 temp_cumul_.clear();
328 for (int path = 0; path < range_of_path_.size(); ++path) {
329 if (committed_range_of_path_[path].Size() == 0) continue;
330 const size_t new_begin = temp_nodes_.size();
331 const auto [begin, end] = committed_range_of_path_[path];
332 temp_nodes_.insert(temp_nodes_.end(), nodes_.begin() + begin,
333 nodes_.begin() + end);
334 temp_transit_.insert(temp_transit_.end(), transit_.begin() + begin,
335 transit_.begin() + end);
336 temp_travel_.insert(temp_travel_.end(), travel_.begin() + begin,
337 travel_.begin() + end);
338 temp_travel_sum_.insert(temp_travel_sum_.end(),
339 travel_sum_.begin() + begin,
340 travel_sum_.begin() + end);
341 temp_cumul_.insert(temp_cumul_.end(), cumul_.begin() + begin,
342 cumul_.begin() + end);
343 committed_range_of_path_[path] = {.begin = new_begin,
344 .end = temp_nodes_.size()};
345 }
346 std::swap(nodes_, temp_nodes_);
347 std::swap(transit_, temp_transit_);
348 std::swap(travel_, temp_travel_);
349 std::swap(travel_sum_, temp_travel_sum_);
350 std::swap(cumul_, temp_cumul_);
351 range_of_path_ = committed_range_of_path_;
352 num_committed_elements_ = nodes_.size();
353 num_current_elements_ = nodes_.size();
354 }
355
356 // Returns a const view of the nodes of the path, in the committed state.
357 absl::Span<const int> CommittedNodes(int path) const {
358 const auto [begin, end] = committed_range_of_path_[path];
359 return absl::MakeConstSpan(nodes_.data() + begin, nodes_.data() + end);
360 }
361
362 // Returns a const view of the nodes of the path, in the current state.
363 absl::Span<const int> Nodes(int path) const {
364 const auto [begin, end] = range_of_path_[path];
365 return absl::MakeConstSpan(nodes_.data() + begin, nodes_.data() + end);
366 }
367
368 // Returns a const view of the transits of the path, in the current state.
369 absl::Span<const Interval> Transits(int path) const {
370 auto [begin, end] = range_of_path_[path];
371 // When the path is not empty, #transits = #nodes - 1.
372 // When the path is empty, begin = end, return empty span.
373 if (begin < end) --end;
374 return absl::MakeConstSpan(transit_.data() + begin, transit_.data() + end);
375 }
376
377 // Returns a mutable view of the transits of the path, in the current state.
378 absl::Span<Interval> MutableTransits(int path) {
379 auto [begin, end] = range_of_path_[path];
380 // When the path is not empty, #transits = #nodes - 1.
381 // When the path is empty, begin = end, return empty span.
382 if (begin < end) --end;
383 return absl::MakeSpan(transit_.data() + begin, transit_.data() + end);
384 }
385
386 // Returns a const view of the travels of the path, in the current
387 // state.
388 absl::Span<const int64_t> Travels(int path) const {
389 auto [begin, end] = range_of_path_[path];
390 if (begin < end) --end;
391 return absl::MakeConstSpan(travel_.data() + begin, travel_.data() + end);
392 }
393
394 // Returns a mutable view of the travels of the path, in the current
395 // state.
396 absl::Span<int64_t> MutableTravels(int path) {
397 auto [begin, end] = range_of_path_[path];
398 if (begin < end) --end;
399 return absl::MakeSpan(travel_.data() + begin, travel_.data() + end);
400 }
401
402 // Returns a const view of the travel sums of the path, in the current state.
403 absl::Span<const int64_t> TravelSums(int path) const {
404 const auto [begin, end] = range_of_path_[path];
405 return absl::MakeConstSpan(travel_sum_.data() + begin,
406 travel_sum_.data() + end);
407 }
408
409 // Returns a mutable view of the travel sums of the path in the current state.
410 absl::Span<int64_t> MutableTravelSums(int path) {
411 const auto [begin, end] = range_of_path_[path];
412 return absl::MakeSpan(travel_sum_.data() + begin, travel_sum_.data() + end);
413 }
414
415 // Returns a const view of the cumul mins of the path, in the current state.
416 absl::Span<const Interval> Cumuls(int path) const {
417 const auto [begin, end] = range_of_path_[path];
418 return absl::MakeConstSpan(cumul_.data() + begin, cumul_.data() + end);
419 }
420
421 // Returns a mutable view of the cumul mins of the path, in the current state.
422 absl::Span<Interval> MutableCumuls(int path) {
423 const auto [begin, end] = range_of_path_[path];
424 return absl::MakeSpan(cumul_.data() + begin, cumul_.data() + end);
425 }
426
427 // Returns the span interval of the path, in the current state.
428 Interval Span(int path) const {
429 return changed_paths_[path] ? span_[path] : committed_span_[path];
430 }
431 // Returns a mutable view of the span of the path, in the current state.
432 // The path must have been changed since the last commit.
434 DCHECK(changed_paths_[path]);
435 return span_[path];
436 }
437
438 // Returns a const view of the vehicle breaks of the path, in the current
439 // state.
440 absl::Span<const VehicleBreak> VehicleBreaks(int path) const {
441 return absl::MakeConstSpan(changed_paths_[path]
442 ? vehicle_breaks_[path]
443 : committed_vehicle_breaks_[path]);
444 }
445
446 // Returns a mutable vector of the vehicle breaks of the path, in the current
447 // state. The path must have been changed since the last commit.
448 std::vector<VehicleBreak>& MutableVehicleBreaks(int path) {
449 DCHECK(changed_paths_[path]);
450 return vehicle_breaks_[path];
451 }
452
453 // Returns the number of nodes of the path, in the current state.
454 int NumNodes(int path) const { return range_of_path_[path].Size(); }
455 // Returns a const view of the set of paths changed, in the current state.
456 absl::Span<const int> ChangedPaths() const {
457 return absl::MakeConstSpan(changed_paths_.PositionsSetAtLeastOnce());
458 }
459 // Returns whether the given path was changed, in the current state.
460 bool PathHasChanged(int path) const { return changed_paths_[path]; }
461
462 private:
463 // These vectors hold the data of both committed and current states.
464 // The ranges below determine which indices are associated to each path and
465 // each state. It is up to the user to maintain the following invariants:
466 // If range_of_path_[p] == {.begin = b, .end = e}, then, in the current
467 // state:
468 // - nodes_[i] for i in [b, e) are the nodes of the path p.
469 // - cumul_[r] + transit_[r] == cumul_[r+1] for r in [b, e-1).
470 // - travel_[r] <= transit_[r].min for r in [b, e-1).
471 // - travel_sum_[r] == sum_{r' in [0, r')} travel_[r'], for r in [b+1, e)
472 // - cumul[b] + span_[p] == cumul[e-1].
473 //
474 // The same invariants should hold for committed_range_of_path_ and the
475 // committed state.
476 std::vector<int> nodes_;
477 std::vector<Interval> transit_;
478 std::vector<int64_t> travel_;
479 std::vector<int64_t> travel_sum_;
480 std::vector<Interval> cumul_;
481 // Temporary vectors used in Commit() during compaction.
482 std::vector<int> temp_nodes_;
483 std::vector<Interval> temp_transit_;
484 std::vector<int64_t> temp_travel_;
485 std::vector<int64_t> temp_travel_sum_;
486 std::vector<Interval> temp_cumul_;
487 // A path has a range of indices in the committed state and another one in the
488 // current state.
489 struct Range {
490 size_t begin = 0;
491 size_t end = 0;
492 int Size() const { return end - begin; }
493 };
494 std::vector<Range> range_of_path_;
495 std::vector<Range> committed_range_of_path_;
496 // Associates span to each path.
497 std::vector<Interval> span_;
498 std::vector<Interval> committed_span_;
499 // Associates vehicle breaks with each path.
500 std::vector<std::vector<VehicleBreak>> vehicle_breaks_;
501 std::vector<std::vector<VehicleBreak>> committed_vehicle_breaks_;
502 // Stores whether each path has been changed since last committed state.
503 SparseBitset<int> changed_paths_;
504 // Threshold for the size of the committed vector. This is purely heuristic:
505 // it should be more than the number of nodes so compactions do not occur at
506 // each submit, but ranges should not be too far apart to avoid cache misses.
507 const size_t max_num_committed_elements_;
508 // This locates the start of new nodes.
509 size_t num_current_elements_ = 0;
510 size_t num_committed_elements_ = 0;
511};
512
513} // namespace operations_research
514
515#endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_FILTER_COMMITTABLES_H_
void Commit()
Makes the current state committed, clearing all changes.
size_t Size() const
Return the size of the vector.
CommittableVector(size_t num_elements, const T &value)
Makes a vector with initial elements all committed to value.
void Set(size_t index, const T &value)
Set the value stored at index in the current state to given value.
T GetCommitted(size_t index) const
Returns a copy of the value stored at index in the last committed state.
const std::vector< size_t > & ChangedIndices() const
void Revert()
Changes the values of the vector to those in the last Commit().
void Reset()
Resets all path to empty, in both committed and current state.
absl::Span< Interval > MutableCumuls(int path)
Returns a mutable view of the cumul mins of the path, in the current state.
int NumNodes(int path) const
Returns the number of nodes of the path, in the current state.
absl::Span< const int64_t > Travels(int path) const
absl::Span< const Interval > Cumuls(int path) const
Returns a const view of the cumul mins of the path, in the current state.
absl::Span< const VehicleBreak > VehicleBreaks(int path) const
void MakePathFromNewNodes(int path)
Turns new nodes into a new path, allocating dimension values for it.
absl::Span< const int > ChangedPaths() const
Returns a const view of the set of paths changed, in the current state.
void Revert()
Clears the changed state, make it point to the committed state.
absl::Span< const Interval > Transits(int path) const
Returns a const view of the transits of the path, in the current state.
absl::Span< const int > Nodes(int path) const
Returns a const view of the nodes of the path, in the current state.
absl::Span< Interval > MutableTransits(int path)
Returns a mutable view of the transits of the path, in the current state.
absl::Span< int64_t > MutableTravelSums(int path)
Returns a mutable view of the travel sums of the path in the current state.
absl::Span< int64_t > MutableTravels(int path)
bool PathHasChanged(int path) const
Returns whether the given path was changed, in the current state.
std::vector< VehicleBreak > & MutableVehicleBreaks(int path)
Interval Span(int path) const
Returns the span interval of the path, in the current state.
void PushNode(int node)
Adds a node to new nodes.
absl::Span< const int > CommittedNodes(int path) const
Returns a const view of the nodes of the path, in the committed state.
absl::Span< const int64_t > TravelSums(int path) const
Returns a const view of the travel sums of the path, in the current state.
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
static Interval AllIntegers()
Returns an interval containing all integers: {kint64min, kint64max}.
bool operator==(const Interval &other) const
Tests equality between intervals.
bool operator!=(const Interval &other) const
Tests inequality between intervals.
bool IsEmpty() const
Returns true iff the interval is empty.