Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
routing_filters.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_FILTERS_H_
15#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_FILTERS_H_
16
17#include <cstdint>
18#include <functional>
19#include <initializer_list>
20#include <memory>
21#include <utility>
22#include <vector>
23
24#include "absl/strings/string_view.h"
25#include "absl/types/span.h"
26#include "ortools/base/types.h"
32#include "ortools/constraint_solver/routing_parameters.pb.h"
34#include "ortools/util/bitset.h"
36
37namespace operations_research {
38
39// Propagates vehicle break constraints in dimension_values.
40// This returns false if breaks cannot fit the path.
41// Otherwise, this returns true, and modifies the start cumul, end cumul and the
42// span of the given path.
43// This applies light reasoning, and runs in O(#breaks * #interbreak rules).
45 int path, DimensionValues& dimension_values,
46 absl::Span<const std::pair<int64_t, int64_t>> interbreaks);
47
50 const RoutingModel& routing_model);
51
54 const RoutingModel& routing_model);
55
59 const RoutingModel& routing_model);
60
63 const RoutingModel& routing_model, bool filter_cost);
64
67 const RoutingModel& routing_model);
68
71 const RoutingModel& routing_model);
72
74IntVarLocalSearchFilter* MakePathCumulFilter(const RoutingDimension& dimension,
75 bool propagate_own_objective_value,
76 bool filter_objective_cost,
77 bool may_use_optimizers);
78
81 const RoutingDimension& dimension);
82
86 GlobalDimensionCumulOptimizer* mp_optimizer, bool filter_objective_cost);
87
92 LocalDimensionCumulOptimizer* mp_optimizer,
93 bool propagate_own_objective_value, bool filter_objective_cost);
94
96IntVarLocalSearchFilter* MakeCPFeasibilityFilter(RoutingModel* routing_model);
97
98#if !defined(SWIG)
99// A PathState represents a set of paths and changes made on it.
100//
101// More accurately, let us define P_{num_nodes, starts, ends}-graphs the set of
102// directed graphs with nodes [0, num_nodes) whose connected components are
103// paths from starts[i] to ends[i] (for the same i) and loops.
104// Let us fix num_nodes, starts and ends, so we call these P-graphs.
105//
106// A P-graph can be described by the sequence of nodes of each of its paths,
107// and its set of loops. To describe a change made on a given P-graph G0 that
108// yields another P-graph G1, we choose to describe G1 in terms of G0. When
109// the difference between G0 and G1 is small, as is almost always the case in a
110// local search setting, the description is compact, allowing for incremental
111// filters to be efficient.
112//
113// In order to describe G1 in terms of G0 succintly, we describe each path of
114// G1 as a sequence of chains of G0. A chain of G0 is either a nonempty sequence
115// of consecutive nodes of a path of G0, or a node that was a loop in G0.
116// For instance, a path that was not modified from G0 to G1 has one chain,
117// the sequence of all nodes in the path. Typically, local search operators
118// modify one or two paths, and the resulting paths can described as sequences
119// of two to four chains of G0. Paths that were modified are listed explicitly,
120// allowing to iterate only on changed paths.
121// The loops of G1 are described more implicitly: the loops of G1 not in G0
122// are listed explicitly, but those in both G1 and G0 are not listed.
123//
124// A PathState object can be in two states: committed or changed.
125// At construction, the object is committed, G0.
126// To enter a changed state G1, one can pass modifications with ChangePath() and
127// ChangeLoops(). For reasons of efficiency, a chain is described as a range of
128// node indices in the representation of the committed graph G0. To that effect,
129// the nodes of a path of G0 are guaranteed to have consecutive indices.
130//
131// Filters can then browse the change efficiently using ChangedPaths(),
132// Chains(), Nodes() and ChangedLoops().
133//
134// Then Commit() or Revert() can be called: Commit() sets the changed state G1
135// as the new committed state, Revert() erases all changes.
137 public:
138 // A Chain allows to iterate on all nodes of a chain, and access some data:
139 // first node, last node, number of nodes in the chain.
140 // Chain is a range, its iterator ChainNodeIterator, its value type int.
141 // Chains are returned by PathChainIterator's operator*().
142 class Chain;
143 // A ChainRange allows to iterate on all chains of a path.
144 // ChainRange is a range, its iterator Chain*, its value type Chain.
145 class ChainRange;
146 // A NodeRange allows to iterate on all nodes of a path.
147 // NodeRange is a range, its iterator PathNodeIterator, its value type int.
148 class NodeRange;
149
157 int CommittedIndex(int node) const { return committed_index_[node]; }
158 ChainBounds CommittedPathRange(int path) const { return chains_[path]; }
159
160 // Path constructor: path_start and path_end must be disjoint,
161 // their values in [0, num_nodes).
162 PathState(int num_nodes, std::vector<int> path_start,
163 std::vector<int> path_end);
164
165 // Instance-constant accessors.
166
167 // Returns the number of nodes in the underlying graph.
168 int NumNodes() const { return num_nodes_; }
169 // Returns the number of paths (empty paths included).
170 int NumPaths() const { return num_paths_; }
171 // Returns the start of a path.
172 int Start(int path) const { return path_start_end_[path].start; }
173 // Returns the end of a path.
174 int End(int path) const { return path_start_end_[path].end; }
175
176 // State-dependent accessors.
177
178 static constexpr int kUnassigned = -2;
179 static constexpr int kLoop = -1;
180 // Returns the committed path of a given node, kLoop if it is a loop,
181 // kUnassigned if it is not assigned,
182 int Path(int node) const { return committed_paths_[node]; }
183 // Returns the set of paths that actually changed,
184 // i.e. that have more than one chain.
185 const std::vector<int>& ChangedPaths() const { return changed_paths_; }
186 // Returns the set of loops that were added by the change.
187 const std::vector<int>& ChangedLoops() const { return changed_loops_; }
188 // Returns the current range of chains of path.
189 ChainRange Chains(int path) const;
190 // Returns the current range of nodes of path.
191 NodeRange Nodes(int path) const;
192
193 // State modifiers.
194
195 // Changes the path to the given sequence of chains of the committed state.
196 // Chains are described by semi-open intervals. No optimization is made in
197 // case two consecutive chains are actually already consecutive in the
198 // committed state: they are not merged into one chain, and Chains(path) will
199 // report the two chains.
200 void ChangePath(int path, absl::Span<const ChainBounds> chains);
201 // Same as above, but the initializer_list interface avoids the need to pass
202 // a vector.
203 void ChangePath(int path, const std::initializer_list<ChainBounds>& chains) {
204 changed_paths_.push_back(path);
205 const int path_begin_index = chains_.size();
206 chains_.insert(chains_.end(), chains.begin(), chains.end());
207 const int path_end_index = chains_.size();
208 paths_[path] = {path_begin_index, path_end_index};
209 // Always add sentinel, in case this is the last path change.
210 chains_.emplace_back(0, 0);
211 }
212
213 // Describes the nodes that are newly loops in this change.
214 void ChangeLoops(absl::Span<const int> new_loops);
215
216 // Set the current state G1 as committed. See class comment for details.
217 void Commit();
218 // Erase incremental changes. See class comment for details.
219 void Revert();
220 // Sets all paths to start->end, all other nodes to kUnassigned.
221 void Reset();
222
223 // LNS Operators may not fix variables,
224 // in which case we mark the candidate invalid.
225 void SetInvalid() { is_invalid_ = true; }
226 bool IsInvalid() const { return is_invalid_; }
227
228 private:
229 // Most structs below are named pairs of ints, for typing purposes.
230
231 // Start and end are stored together to optimize (likely) simultaneous access.
232 struct PathStartEnd {
233 PathStartEnd(int start, int end) : start(start), end(end) {}
234 int start;
235 int end;
236 };
237 // Paths are ranges of chains, which are ranges of committed nodes, see below.
238 struct PathBounds {
239 int begin_index;
240 int end_index;
241 };
242
243 // Copies nodes in chains of path at the end of nodes,
244 // and sets those nodes' path member to value path.
245 void CopyNewPathAtEndOfNodes(int path);
246 // Commits paths in O(#{changed paths' nodes}) time,
247 // increasing this object's space usage by O(|changed path nodes|).
248 void IncrementalCommit();
249 // Commits paths in O(num_nodes + num_paths) time,
250 // reducing this object's space usage to O(num_nodes + num_paths).
251 void FullCommit();
252
253 // Instance-constant data.
254 const int num_nodes_;
255 const int num_paths_;
256 std::vector<PathStartEnd> path_start_end_;
257
258 // Representation of the committed and changed paths.
259 // A path is a range of chains, which is a range of nodes.
260 // Ranges are represented internally by indices in vectors:
261 // ChainBounds are indices in committed_nodes_. PathBounds are indices in
262 // chains_. When committed (after construction, Revert() or Commit()):
263 // - path ranges are [path, path+1): they have one chain.
264 // - chain ranges don't overlap, chains_ has an empty sentinel at the end.
265 // The sentinel allows the Nodes() iterator to maintain its current pointer
266 // to committed nodes on NodeRange::operator++().
267 // - committed_nodes_ contains all nodes, both paths and loops.
268 // Actually, old duplicates will likely appear,
269 // the current version of a node is at the index given by
270 // committed_index_[node]. A Commit() can add nodes at the end of
271 // committed_nodes_ in a space/time tradeoff, but if committed_nodes_' size
272 // is above num_nodes_threshold_, Commit() must reclaim useless duplicates'
273 // space by rewriting the path/chain/nodes structure.
274 // When changed (after ChangePaths() and ChangeLoops()),
275 // the structure is updated accordingly:
276 // - path ranges that were changed have nonoverlapping values [begin, end)
277 // where begin is >= num_paths_ + 1, i.e. new chains are stored after
278 // the committed state.
279 // - additional chain ranges are stored after the committed chains and its
280 // sentinel to represent the new chains resulting from the changes.
281 // Those chains do not overlap with one another or with committed chains.
282 // - committed_nodes_ are not modified, and still represent the committed
283 // paths. committed_index_ is not modified either.
284 std::vector<int> committed_nodes_;
285 // Maps nodes to their path in the latest committed state.
286 std::vector<int> committed_paths_;
287 // Maps nodes to their index in the latest committed state.
288 std::vector<int> committed_index_;
289 const int num_nodes_threshold_;
290 std::vector<ChainBounds> chains_;
291 std::vector<PathBounds> paths_;
292
293 // Incremental information.
294 std::vector<int> changed_paths_;
295 std::vector<int> changed_loops_;
296
297 // See IsInvalid() and SetInvalid().
298 bool is_invalid_ = false;
299};
300
301// A Chain is a range of committed nodes.
303 public:
304 class Iterator {
305 public:
306 Iterator& operator++() {
307 ++current_node_;
308 return *this;
309 }
310 int operator*() const { return *current_node_; }
311 bool operator!=(Iterator other) const {
312 return current_node_ != other.current_node_;
313 }
314
315 private:
316 // Only a Chain can construct its iterator.
317 friend class PathState::Chain;
318 explicit Iterator(const int* node) : current_node_(node) {}
319 const int* current_node_;
320 };
321
322 // Chains hold CommittedNode* values, a Chain may be invalidated
323 // if the underlying vector is modified.
324 Chain(const int* begin_node, const int* end_node)
325 : begin_(begin_node), end_(end_node) {}
326
327 int NumNodes() const { return end_ - begin_; }
328 int First() const { return *begin_; }
329 int Last() const { return *(end_ - 1); }
330 Iterator begin() const { return Iterator(begin_); }
331 Iterator end() const { return Iterator(end_); }
332
333 Chain WithoutFirstNode() const { return Chain(begin_ + 1, end_); }
334
335 private:
336 const int* const begin_;
337 const int* const end_;
338};
339
340// A ChainRange is a range of Chains, committed or not.
342 public:
343 class Iterator {
344 public:
345 Iterator& operator++() {
346 ++current_chain_;
347 return *this;
348 }
349 Chain operator*() const {
350 return {first_node_ + current_chain_->begin_index,
351 first_node_ + current_chain_->end_index};
352 }
353 bool operator!=(Iterator other) const {
354 return current_chain_ != other.current_chain_;
355 }
356
357 private:
358 // Only a ChainRange can construct its Iterator.
359 friend class ChainRange;
360 Iterator(const ChainBounds* chain, const int* const first_node)
361 : current_chain_(chain), first_node_(first_node) {}
362 const ChainBounds* current_chain_;
363 const int* const first_node_;
364 };
365
366 // ChainRanges hold ChainBounds* and CommittedNode*,
367 // a ChainRange may be invalidated if on of the underlying vector is modified.
368 ChainRange(const ChainBounds* const begin_chain,
369 const ChainBounds* const end_chain, const int* const first_node)
370 : begin_(begin_chain), end_(end_chain), first_node_(first_node) {}
371
372 Iterator begin() const { return {begin_, first_node_}; }
373 Iterator end() const { return {end_, first_node_}; }
374
376 return (begin_ == end_) ? *this : ChainRange(begin_ + 1, end_, first_node_);
377 }
378
380 return (begin_ == end_) ? *this : ChainRange(begin_, end_ - 1, first_node_);
381 }
382
383 private:
384 const ChainBounds* const begin_;
385 const ChainBounds* const end_;
386 const int* const first_node_;
387};
388
389// A NodeRange allows to iterate on all nodes of a path,
390// by a two-level iteration on ChainBounds* and CommittedNode* of a PathState.
392 public:
393 class Iterator {
394 public:
395 Iterator& operator++() {
396 ++current_node_;
397 if (current_node_ == end_node_) {
398 ++current_chain_;
399 // Note: dereferencing bounds is valid because there is a sentinel
400 // value at the end of PathState::chains_ to that intent.
401 const ChainBounds bounds = *current_chain_;
402 current_node_ = first_node_ + bounds.begin_index;
403 end_node_ = first_node_ + bounds.end_index;
404 }
405 return *this;
406 }
407 int operator*() const { return *current_node_; }
408 bool operator!=(Iterator other) const {
409 return current_chain_ != other.current_chain_;
410 }
411
412 private:
413 // Only a NodeRange can construct its Iterator.
414 friend class NodeRange;
415 Iterator(const ChainBounds* current_chain, const int* const first_node)
416 : current_node_(first_node + current_chain->begin_index),
417 end_node_(first_node + current_chain->end_index),
418 current_chain_(current_chain),
419 first_node_(first_node) {}
420 const int* current_node_;
421 const int* end_node_;
422 const ChainBounds* current_chain_;
423 const int* const first_node_;
424 };
425
426 // NodeRanges hold ChainBounds* and int* (first committed node),
427 // a NodeRange may be invalidated if on of the underlying vector is modified.
428 NodeRange(const ChainBounds* begin_chain, const ChainBounds* end_chain,
429 const int* first_node)
430 : begin_chain_(begin_chain),
431 end_chain_(end_chain),
432 first_node_(first_node) {}
433 Iterator begin() const { return {begin_chain_, first_node_}; }
434 // Note: there is a sentinel value at the end of PathState::chains_,
435 // so dereferencing chain_range_.end()->begin_ is always valid.
436 Iterator end() const { return {end_chain_, first_node_}; }
437
438 private:
439 const ChainBounds* begin_chain_;
440 const ChainBounds* end_chain_;
441 const int* const first_node_;
442};
443
444// Make a filter that takes ownership of a PathState and synchronizes it with
445// solver events. The solver represents a graph with array of variables 'nexts'.
446// Solver events are embodied by Assignment* deltas, that are translated to node
447// changes during Relax(), committed during Synchronize(), and reverted on
448// Revert().
450 std::unique_ptr<PathState> path_state,
451 const std::vector<IntVar*>& nexts);
452
454LocalSearchFilter* MakeVehicleVarFilter(const RoutingModel& routing_model,
455 const PathState* path_state);
456
460 const RoutingModel& routing_model, const PathState* path_state,
461 const std::vector<PickupDeliveryPair>& pairs,
462 const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies);
463
464// This checker enforces dimension requirements.
465// A dimension requires that there is some valuation of
466// cumul and demand such that for all paths:
467// - cumul[A] is in interval node_capacity[A]
468// - if arc A -> B is on a path of path_class p,
469// then cumul[A] + demand[p](A, B) = cumul[B].
470// - if A is on a path of class p, then
471// cumul[A] must be inside interval path_capacity[path].
473 public:
474 struct Interval {
475 int64_t min;
476 int64_t max;
477 };
478
480 int64_t min;
481 int64_t max;
484 };
485
486 // TODO(user): the addition of kMinRangeSizeForRIQ slowed down Check().
487 // See if using a template parameter makes it faster.
488 DimensionChecker(const PathState* path_state,
489 std::vector<Interval> path_capacity,
490 std::vector<int> path_class,
491 std::vector<std::function<Interval(int64_t, int64_t)>>
492 demand_per_path_class,
493 std::vector<Interval> node_capacity,
494 int min_range_size_for_riq = kOptimalMinRangeSizeForRIQ);
495
496 // Given the change made in PathState, checks that the dimension
497 // constraint is still feasible.
498 bool Check() const;
499
500 // Commits to the changes made in PathState,
501 // must be called before PathState::Commit().
502 void Commit();
503
504 static constexpr int kOptimalMinRangeSizeForRIQ = 4;
505
506 private:
507 inline void UpdateCumulUsingChainRIQ(int first_index, int last_index,
508 const ExtendedInterval& path_capacity,
509 ExtendedInterval& cumul) const;
510
511 // Commits to the current solution and rebuilds structures from scratch.
512 void FullCommit();
513 // Commits to the current solution and only build structures for paths that
514 // changed, using additional space to do so in a time-memory tradeoff.
515 void IncrementalCommit();
516 // Adds sums of given path to the bottom layer of the Range Intersection Query
517 // structure, updates index_ and previous_nontrivial_index_.
518 void AppendPathDemandsToSums(int path);
519 // Updates the Range Intersection Query structure from its bottom layer,
520 // with [begin_index, end_index) the range of the change,
521 // which must be at the end of the bottom layer.
522 // Supposes that requests overlapping the range will be inside the range,
523 // to avoid updating all layers.
524 void UpdateRIQStructure(int begin_index, int end_index);
525
526 const PathState* const path_state_;
527 const std::vector<ExtendedInterval> path_capacity_;
528 const std::vector<int> path_class_;
529 const std::vector<std::function<Interval(int64_t, int64_t)>>
530 demand_per_path_class_;
531 std::vector<ExtendedInterval> cached_demand_;
532 const std::vector<ExtendedInterval> node_capacity_;
533
534 // Precomputed data.
535 // Maps nodes to their pre-computed data, except for isolated nodes,
536 // which do not have precomputed data.
537 // Only valid for nodes that are in some path in the committed state.
538 std::vector<int> index_;
539 // Range intersection query in <O(n log n), O(1)>, with n = #nodes.
540 // Let node be in a path, i = index_[node], start the start of node's path.
541 // Let l such that index_[start] <= i - 2**l.
542 // - riq_[l][i].tsum_at_lst contains the sum of demands from start to node.
543 // - riq_[l][i].tsum_at_fst contains the sum of demands from start to the
544 // node at i - 2**l.
545 // - riq_[l][i].tightest_tsum contains the intersection of
546 // riq_[0][j].tsum_at_lst for all j in (i - 2**l, i].
547 // - riq_[0][i].cumuls_to_lst and riq_[0][i].cumuls_to_fst contain
548 // the node's capacity.
549 // - riq_[l][i].cumuls_to_lst is the intersection, for j in (i - 2**l, i], of
550 // riq_[0][j].cumuls_to_lst + sum_{k in [j, i)} demand(k, k+1)
551 // - riq_[l][i].cumuls_to_fst is the intersection, for j in (i - 2**l, i], of
552 // riq_[0][j].cumuls_to_fst - sum_{k in (i-2**l, j)} demand(k, k+1)
553 struct RIQNode {
554 ExtendedInterval cumuls_to_fst;
555 ExtendedInterval tightest_tsum;
556 ExtendedInterval cumuls_to_lst;
557 ExtendedInterval tsum_at_fst;
558 ExtendedInterval tsum_at_lst;
559 };
560 std::vector<std::vector<RIQNode>> riq_;
561 // The incremental branch of Commit() may waste space in the layers of the
562 // RIQ structure. This is the upper limit of a layer's size.
563 const int maximum_riq_layer_size_;
564 // Range queries are used on a chain only if the range is larger than this.
565 const int min_range_size_for_riq_;
566};
567
568// Make a filter that translates solver events to the input checker's interface.
569// Since DimensionChecker has a PathState, the filter returned by this
570// must be synchronized to the corresponding PathStateFilter:
571// - Relax() must be called after the PathStateFilter's.
572// - Accept() must be called after.
573// - Synchronize() must be called before.
574// - Revert() must be called before.
575LocalSearchFilter* MakeDimensionFilter(
576 Solver* solver, std::unique_ptr<DimensionChecker> checker,
577 absl::string_view dimension_name);
578#endif // !defined(SWIG)
579
581 public:
583 int64_t start_min;
584 int64_t start_max;
585 int64_t end_min;
586 int64_t end_max;
590 };
595
604
606 std::vector<PathData> path_data);
607
608 void Relax() const;
609
610 bool Check() const;
611
612 private:
613 PathState* path_state_;
614 std::vector<PathData> path_data_;
615};
616
618 Solver* solver, std::unique_ptr<LightVehicleBreaksChecker> checker,
619 absl::string_view dimension_name);
620
621// This class allows making fast range queries on sequences of elements.
622// * Main characteristics.
623// - queries on sequences of elements {height, weight},
624// parametrized by (begin, end, T), returning
625// sum_{i \in [begin, end), S[i].height >= T} S[i].weight
626// - O(log (#different heights)) time complexity thanks to an underlying
627// wavelet tree (https://en.wikipedia.org/wiki/Wavelet_Tree)
628// - holds several sequences at once, can be cleared while still keeping
629// allocated memory to avoid allocations.
630// More details on these points follow.
631//
632// * Query complexity.
633// The time complexity of a query in S is O(log H), where H is the number of
634// different heights appearing in S.
635// The particular implementation guarantees that queries that are trivial in
636// the .height dimension, that is if threshold_height is <= or >= all heights
637// in the range, are O(1).
638//
639// * Initialization complexity.
640// The time complexity of filling the underlying data structures,
641// which is done by running MakeTreeFromNewElements(),
642// is O(N log N) where N is the number of new elements.
643// The space complexity is a O(N log H).
644//
645// * Usage.
646// Given Histogram holding elements with fields {.height, .weight},
647// Histogram hist1 {{2, 3}, {1, 4}, {4, 1}, {2, 2}, {3, 1}, {0, 4}};
648// Histogram hist2 {{-2, -3}, {-1, -4}, {-4, -1}, {-2, -2}};
649// WeightedWaveletTree tree;
650//
651// for (const auto [height, weight] : hist1]) {
652// tree.PushBack(height, weight);
653// }
654// const int begin1 = tree.TreeSize();
655// tree.MakeTreeFromNewElements();
656// const int end1 = tree.TreeSize();
657// const int begin2 = tree.TreeSize(); // begin2 == end1.
658// for (const auto [height, weight] : hist2]) {
659// tree.PushBack(height, weight);
660// }
661// tree.MakeTreeFromNewElements();
662// const int end2 = tree.TreeSize();
663//
664// // Sum of weights on whole first sequence, == 3 + 4 + 1 + 2 + 1 + 4
665// tree.RangeSumWithThreshold(/*threshold=*/0, /*begin=*/begin1, /*end=*/end1);
666// // Sum of weights on whole second sequence, all heights are negative,
667// // so the result is 0.
668// tree.RangeSumWithThreshold(/*threshold=*/0, /*begin=*/begin2, /*end=*/end2);
669// // This is forbidden, because the range overlaps two sequences.
670// tree.RangeSumWithThreshold(/*threshold=*/0, /*begin=*/2, /*end=*/10);
671// // Returns 2 = 0 + 1 + 0 + 1.
672// tree.RangeSumWithThreshold(/*threshold=*/3, /*begin=*/1, /*end=*/5);
673// // Returns -6 = -4 + 0 + -2.
674// tree.RangeSumWithThreshold(/*threshold=*/-2, /*begin=*/1, /*end=*/4);
675// // Add another sequence.
676// Histogram hist3 {{1, 1}, {3, 4}};
677// const int begin3 = tree.TreeSize();
678// for (const auto [height, weight] : hist3) {
679// tree.PushBack(height, weight);
680// }
681// tree.MakeTreeFromNewElements();
682// const int end3 = tree.TreeSize();
683// // Returns 4 = 0 + 4.
684// tree.RangeSumWithThreshold(/*threshold=*/2, /*begin=*/begin3, /*end=*/end3);
685// // Clear the tree, this invalidates all range queries.
686// tree.Clear();
687// // Forbidden!
688// tree.RangeSumWithThreshold(/*threshold=*/2, /*begin=*/begin3, /*end=*/end3);
689//
690// * Implementation.
691// This data structure uses two main techniques of the wavelet tree:
692// - a binary search tree in the height dimension.
693// - nodes only hold information about elements in their height range,
694// keeping selected elements in the same order as the full sequence,
695// and can map the index of its elements to their left and right child.
696// The layout of the tree is packed by separating the tree navigation
697// information from the (prefix sum + mapping) information.
698// Here is how the tree for heights 6 4 1 3 6 1 7 4 2 is laid out in memory:
699// tree_layers_ // nodes_
700// 6 4 1 3 6 1 7 4 2 // 4
701// 1 3 1 2|6 4 6 7 4 // 2 6
702// 1 1|3 2|4 4|6 6 7 // _ 3 _ 7
703// _ _|2|3|_ _|6 6|7 // Dummy information is used to pad holes in nodes_.
704// In addition to the mapping information of each element, each node holds
705// the prefix sum of weights up to each element, to be able to compute the sum
706// of S[i].weight of elements in its height range, for any range, in O(1).
707// The data structure does not actually need height information inside the tree
708// nodes, and does not store them.
710 public:
712
713 // Clears all trees, which invalidates all further range queries on currently
714 // existing trees. This does *not* release memory held by this object.
715 void Clear();
716
717 // Returns the total number of elements in trees.
718 int TreeSize() const { return tree_location_.size(); }
719
720 // Adds an element at index this->Size().
721 void PushBack(int64_t height, int64_t weight) {
722 elements_.push_back({.height = height, .weight = weight});
723 }
724
725 // Generates the wavelet tree for all new elements, i.e. elements that were
726 // added with PushBack() since the latest of these events: construction of
727 // this object, a previous call to MakeTreeFromNewElements(), or a call to
728 // Clear().
729 // The range of new elements [begin, end), with begin the Size() at the
730 // latest event, and end the current Size().
732
733 // Returns sum_{begin_index <= i < end_index,
734 // S[i].height >= threshold_height} S[i].weight.
735 // The range [begin_index, end_index) can only cover elements that were new
736 // at the same call to MakeTreeFromNewElements().
737 // When calling this method, there must be no pending new elements,
738 // i.e. the last method called must not have been PushBack() or TreeSize().
739 int64_t RangeSumWithThreshold(int64_t threshold_height, int begin_index,
740 int end_index) const;
741
742 private:
743 // Internal copy of an element.
744 struct Element {
745 int64_t height;
746 int64_t weight;
747 };
748 // Elements are stored in a vector, they are only used during the
749 // initialization of the data structure.
750 std::vector<Element> elements_;
751
752 // Maps the index of an element to the location of its tree.
753 // Elements of the same sequence have the same TreeLocation value.
754 struct TreeLocation {
755 int node_begin; // index of the first node in the tree in nodes_.
756 int node_end; // index of the last node in the tree in nodes_, plus 1.
757 int sequence_first; // index of the first element in all layers.
758 };
759 std::vector<TreeLocation> tree_location_;
760
761 // A node of the tree is represented by the height of its pivot element and
762 // the index of its pivot in the layer below, or -1 if the node is a leaf.
763 struct Node {
764 int64_t pivot_height;
765 int pivot_index;
766 bool operator<(const Node& other) const {
767 return pivot_height < other.pivot_height;
768 }
769 bool operator==(const Node& other) const {
770 return pivot_height == other.pivot_height;
771 }
772 };
773 std::vector<Node> nodes_;
774
775 // Holds range sum query and mapping information of each element
776 // in each layer.
777 // - prefix_sum: sum of weights in this node up to this element, included.
778 // - left_index: number of elements in the same layer that are either:
779 // - in a node on the left of this node, or
780 // - in the same node, preceding this element, mapped to the left subtree.
781 // Coincides with this element's index in the left subtree if is_left = 1.
782 // - is_left: 1 if the element is in the left subtree, otherwise 0.
783 struct ElementInfo {
784 int64_t prefix_sum;
785 int left_index : 31;
786 unsigned int is_left : 1;
787 };
788 // Contains range sum query and mapping data of all elements in their
789 // respective tree, arranged by layer (depth) in the tree.
790 // Layer 0 has root data, layer 1 has information of the left child
791 // then the right child, layer 2 has left-left, left-right, right-left,
792 // then right-right, etc.
793 // Trees are stored consecutively, e.g. in each layer, the tree resulting
794 // from the second MakeTreeFromNewElements() has its root information
795 // after that of the tree resulting from the first MakeTreeFromNewElements().
796 // If a node does not exist, some padding is stored instead.
797 // Padding allows all layers to store the same number of element information,
798 // which is one ElementInfo per element of the original sequence.
799 // The values necessary to navigate the tree are stored in a separate
800 // structure, in tree_location_ and nodes_.
801 std::vector<std::vector<ElementInfo>> tree_layers_;
802
803 // Represents a range of elements inside a node of a wavelet tree.
804 // Also provides methods to compute the range sum query corresponding to
805 // the range, and to project the range to left and right children.
806 struct ElementRange {
807 int range_first_index;
808 int range_last_index; // Last element of the range, inclusive.
809 // True when the first element of this range is the first element of the
810 // node. This is tracked to avoid out-of-bounds indices when computing range
811 // sum queries from prefix sums.
812 bool range_first_is_node_first;
813
814 bool Empty() const { return range_first_index > range_last_index; }
815
816 int64_t Sum(const ElementInfo* elements) const {
817 return elements[range_last_index].prefix_sum -
818 (range_first_is_node_first
819 ? 0
820 : elements[range_first_index - 1].prefix_sum);
821 }
822
823 ElementRange RightSubRange(const ElementInfo* els, int pivot_index) const {
824 ElementRange right = {
825 .range_first_index =
826 pivot_index +
827 (range_first_index - els[range_first_index].left_index),
828 .range_last_index =
829 pivot_index +
830 (range_last_index - els[range_last_index].left_index) -
831 static_cast<int>(els[range_last_index].is_left),
832 .range_first_is_node_first = false};
833 right.range_first_is_node_first = right.range_first_index == pivot_index;
834 return right;
835 }
836
837 ElementRange LeftSubRange(const ElementInfo* els) const {
838 return {.range_first_index = els[range_first_index].left_index,
839 .range_last_index = els[range_last_index].left_index -
840 !els[range_last_index].is_left,
841 .range_first_is_node_first = range_first_is_node_first};
842 }
843 };
844};
845
846// TODO(user): improve this class by:
847// - using WeightedWaveletTree to get the amount of energy above the threshold.
848// - detect when costs above and below are the same, to avoid correcting for
849// energy above the threshold and get O(1) time per chain.
851 public:
862 const PathState* path_state, std::vector<int64_t> force_start_min,
863 std::vector<int64_t> force_end_min, std::vector<int> force_class,
864 std::vector<const std::function<int64_t(int64_t)>*> force_per_class,
865 std::vector<int> distance_class,
866 std::vector<const std::function<int64_t(int64_t, int64_t)>*>
867 distance_per_class,
868 std::vector<EnergyCost> path_energy_cost,
869 std::vector<bool> path_has_cost_when_empty);
870 bool Check();
871 void Commit();
872 int64_t CommittedCost() const { return committed_total_cost_; }
873 int64_t AcceptedCost() const { return accepted_total_cost_; }
874
875 private:
876 int64_t ComputePathCost(int64_t path) const;
877 void CacheAndPrecomputeRangeQueriesOfPath(int path);
878 void IncrementalCacheAndPrecompute();
879 void FullCacheAndPrecompute();
880
881 const PathState* const path_state_;
882 const std::vector<int64_t> force_start_min_;
883 const std::vector<int64_t> force_end_min_;
884 const std::vector<int> force_class_;
885 const std::vector<int> distance_class_;
886 const std::vector<const std::function<int64_t(int64_t)>*> force_per_class_;
887 const std::vector<const std::function<int64_t(int64_t, int64_t)>*>
888 distance_per_class_;
889 const std::vector<EnergyCost> path_energy_cost_;
890 const std::vector<bool> path_has_cost_when_empty_;
891
892 // Range queries.
893 const int maximum_range_query_size_;
894 // Allows to compute the minimum total_force over any chain,
895 // supposing the starting force is 0.
897 std::vector<int> force_rmq_index_of_node_;
898 // Allows to compute the sum of energies of transitions whose total_force is
899 // above a threshold over any chain. Supposes total_force at start is 0.
900 WeightedWaveletTree energy_query_;
901 // Allows to compute the sum of distances of transitions whose total_force is
902 // above a threshold over any chain. Supposes total_force at start is 0.
903 WeightedWaveletTree distance_query_;
904 // Maps nodes to their common index in both threshold queries.
905 std::vector<int> threshold_query_index_of_node_;
906
907 std::vector<int64_t> cached_force_;
908 std::vector<int64_t> cached_distance_;
909
910 // Incremental cost computation.
911 int64_t committed_total_cost_;
912 int64_t accepted_total_cost_;
913 std::vector<int64_t> committed_path_cost_;
914};
915
916LocalSearchFilter* MakePathEnergyCostFilter(
917 Solver* solver, std::unique_ptr<PathEnergyCostChecker> checker,
918 absl::string_view dimension_name);
919
923 const PathState* path_state,
924 const std::vector<RoutingDimension*>& dimensions,
925 std::vector<LocalSearchFilterManager::FilterEvent>* filters);
926
928 const std::vector<RoutingDimension*>& dimensions,
929 const RoutingSearchParameters& parameters, bool filter_objective_cost,
930 bool use_chain_cumul_filter,
931 std::vector<LocalSearchFilterManager::FilterEvent>* filters);
932
934
936 public:
937 BasePathFilter(const std::vector<IntVar*>& nexts, int next_domain_size,
938 const PathsMetadata& paths_metadata);
939 ~BasePathFilter() override = default;
940 bool Accept(const Assignment* delta, const Assignment* deltadelta,
941 int64_t objective_min, int64_t objective_max) override;
942 void OnSynchronize(const Assignment* delta) override;
943
944 protected:
945 static const int64_t kUnassigned;
946
947 int64_t GetNext(int64_t node) const {
948 return (new_nexts_[node] == kUnassigned)
949 ? (IsVarSynced(node) ? Value(node) : kUnassigned)
950 : new_nexts_[node];
951 }
952 bool HasAnySyncedPath() const {
953 for (int64_t start : paths_metadata_.Starts()) {
954 if (IsVarSynced(start)) return true;
955 }
956 return false;
957 }
958 int NumPaths() const { return paths_metadata_.NumPaths(); }
959 int64_t Start(int i) const { return paths_metadata_.Start(i); }
960 int64_t End(int i) const { return paths_metadata_.End(i); }
961 int GetPath(int64_t node) const { return paths_metadata_.GetPath(node); }
962 int Rank(int64_t node) const { return ranks_[node]; }
963 const std::vector<int64_t>& GetTouchedPathStarts() const {
964 return touched_paths_.PositionsSetAtLeastOnce();
965 }
966 bool PathStartTouched(int64_t start) const { return touched_paths_[start]; }
967 const std::vector<int64_t>& GetNewSynchronizedUnperformedNodes() const {
968 return new_synchronized_unperformed_nodes_.PositionsSetAtLeastOnce();
969 }
970
971 bool lns_detected() const { return lns_detected_; }
972
973 private:
974 virtual void OnBeforeSynchronizePaths(bool) {}
975 virtual void OnAfterSynchronizePaths() {}
976 virtual void OnSynchronizePathFromStart(int64_t) {}
977 virtual bool InitializeAcceptPath() { return true; }
978 virtual bool AcceptPath(int64_t path_start, int64_t chain_start,
979 int64_t chain_end) = 0;
980 virtual bool FinalizeAcceptPath(int64_t, int64_t) { return true; }
982 void ComputePathStarts(std::vector<int64_t>* path_starts,
983 std::vector<int>* index_to_path);
984 bool HavePathsChanged();
985 void SynchronizeFullAssignment();
986 void UpdateAllRanks();
987 void UpdatePathRanksFromStart(int start);
988
989 const PathsMetadata& paths_metadata_;
990 std::vector<int64_t> node_path_starts_;
991 SparseBitset<int64_t> new_synchronized_unperformed_nodes_;
992 std::vector<int64_t> new_nexts_;
993 std::vector<int> delta_touched_;
994 SparseBitset<> touched_paths_;
995 // clang-format off
996 std::vector<std::pair<int64_t, int64_t> > touched_path_chain_start_ends_;
997 // clang-format on
998 std::vector<int> ranks_;
999
1000 bool lns_detected_;
1001};
1002
1003} // namespace operations_research
1004
1005#endif // OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_FILTERS_H_
bool PathStartTouched(int64_t start) const
BasePathFilter(const std::vector< IntVar * > &nexts, int next_domain_size, const PathsMetadata &paths_metadata)
int64_t GetNext(int64_t node) const
const std::vector< int64_t > & GetTouchedPathStarts() const
bool Accept(const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max) override
const std::vector< int64_t > & GetNewSynchronizedUnperformedNodes() const
void OnSynchronize(const Assignment *delta) override
DimensionChecker(const PathState *path_state, std::vector< Interval > path_capacity, std::vector< int > path_class, std::vector< std::function< Interval(int64_t, int64_t)> > demand_per_path_class, std::vector< Interval > node_capacity, int min_range_size_for_riq=kOptimalMinRangeSizeForRIQ)
static constexpr int kOptimalMinRangeSizeForRIQ
IntVarLocalSearchFilter(const std::vector< IntVar * > &vars)
LightVehicleBreaksChecker(PathState *path_state, std::vector< PathData > path_data)
PathEnergyCostChecker(const PathState *path_state, std::vector< int64_t > force_start_min, std::vector< int64_t > force_end_min, std::vector< int > force_class, std::vector< const std::function< int64_t(int64_t)> * > force_per_class, std::vector< int > distance_class, std::vector< const std::function< int64_t(int64_t, int64_t)> * > distance_per_class, std::vector< EnergyCost > path_energy_cost, std::vector< bool > path_has_cost_when_empty)
friend class ChainRange
Only a ChainRange can construct its Iterator.
A ChainRange is a range of Chains, committed or not.
ChainRange(const ChainBounds *const begin_chain, const ChainBounds *const end_chain, const int *const first_node)
A Chain is a range of committed nodes.
Chain(const int *begin_node, const int *end_node)
friend class NodeRange
Only a NodeRange can construct its Iterator.
NodeRange(const ChainBounds *begin_chain, const ChainBounds *end_chain, const int *first_node)
int NumNodes() const
Instance-constant accessors.
void ChangeLoops(absl::Span< const int > new_loops)
Describes the nodes that are newly loops in this change.
const std::vector< int > & ChangedLoops() const
Returns the set of loops that were added by the change.
void Reset()
Sets all paths to start->end, all other nodes to kUnassigned.
const std::vector< int > & ChangedPaths() const
void Commit()
Set the current state G1 as committed. See class comment for details.
ChainBounds CommittedPathRange(int path) const
ChainRange Chains(int path) const
Returns the current range of chains of path.
static constexpr int kUnassigned
State-dependent accessors.
int NumPaths() const
Returns the number of paths (empty paths included).
void Revert()
Erase incremental changes. See class comment for details.
NodeRange Nodes(int path) const
Returns the current range of nodes of path.
int End(int path) const
Returns the end of a path.
PathState(int num_nodes, std::vector< int > path_start, std::vector< int > path_end)
void ChangePath(int path, const std::initializer_list< ChainBounds > &chains)
int CommittedIndex(int node) const
void ChangePath(int path, absl::Span< const ChainBounds > chains)
State modifiers.
int Start(int path) const
Returns the start of a path.
For the time being, Solver is neither MT_SAFE nor MT_HOT.
void PushBack(int64_t height, int64_t weight)
Adds an element at index this->Size().
int64_t RangeSumWithThreshold(int64_t threshold_height, int begin_index, int end_index) const
int TreeSize() const
Returns the total number of elements in trees.
In SWIG mode, we don't want anything besides these top-level includes.
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
Returns a filter computing vehicle amortized costs.
IntVarLocalSearchFilter * MakeRouteConstraintFilter(const RoutingModel &routing_model)
Returns a filter tracking route constraints.
LocalSearchFilter * MakePickupDeliveryFilter(const RoutingModel &routing_model, const PathState *path_state, const std::vector< PickupDeliveryPair > &pairs, const std::vector< RoutingModel::PickupAndDeliveryPolicy > &vehicle_policies)
IntVarLocalSearchFilter * MakeMaxActiveVehiclesFilter(const RoutingModel &routing_model)
Returns a filter ensuring that max active vehicles constraints are enforced.
void AppendLightWeightDimensionFilters(const PathState *path_state, const std::vector< RoutingDimension * > &dimensions, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
IntVarLocalSearchFilter * MakeNodeDisjunctionFilter(const RoutingModel &routing_model, bool filter_cost)
Returns a filter ensuring that node disjunction constraints are enforced.
LocalSearchFilter * MakePathEnergyCostFilter(Solver *solver, std::unique_ptr< PathEnergyCostChecker > checker, absl::string_view dimension_name)
IntVarLocalSearchFilter * MakeCumulBoundsPropagatorFilter(const RoutingDimension &dimension)
Returns a filter handling dimension cumul bounds.
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
LocalSearchFilter * MakeLightVehicleBreaksFilter(Solver *solver, std::unique_ptr< LightVehicleBreaksChecker > checker, absl::string_view dimension_name)
IntVarLocalSearchFilter * MakePathCumulFilter(const RoutingDimension &dimension, bool propagate_own_objective_value, bool filter_objective_cost, bool may_use_optimizers)
Returns a filter handling dimension costs and constraints.
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(RoutingModel *routing_model)
Returns a filter checking the current solution using CP propagation.
LocalSearchFilter * MakeVehicleVarFilter(const RoutingModel &routing_model, const PathState *path_state)
Returns a filter checking that vehicle variable domains are respected.
void AppendDimensionCumulFilters(const std::vector< RoutingDimension * > &dimensions, const RoutingSearchParameters &parameters, bool filter_objective_cost, bool use_chain_cumul_filter, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
LocalSearchFilter * MakeResourceAssignmentFilter(LocalDimensionCumulOptimizer *lp_optimizer, LocalDimensionCumulOptimizer *mp_optimizer, bool propagate_own_objective_value, bool filter_objective_cost)
bool PropagateLightweightVehicleBreaks(int path, DimensionValues &dimension_values, absl::Span< const std::pair< int64_t, int64_t > > interbreaks)
IntVarLocalSearchFilter * MakeTypeRegulationsFilter(const RoutingModel &routing_model)
Returns a filter ensuring type regulation constraints are enforced.
IntVarLocalSearchFilter * MakeGlobalLPCumulFilter(GlobalDimensionCumulOptimizer *lp_optimizer, GlobalDimensionCumulOptimizer *mp_optimizer, bool filter_objective_cost)
Returns a filter checking global linear constraints and costs.
util_intops::StrongIntRange< ElementIndex > ElementRange
IntVarLocalSearchFilter * MakeActiveNodeGroupFilter(const RoutingModel &routing_model)
LocalSearchFilter * MakeDimensionFilter(Solver *solver, std::unique_ptr< DimensionChecker > checker, absl::string_view dimension_name)
ChainBounds(int begin_index, int end_index)
static const int64_t kint64max
Definition types.h:32